Add Uno.WinUI3Convert (#2)
This commit is contained in:
Родитель
c1c791e6f8
Коммит
4cf0918ee0
|
@ -0,0 +1,63 @@
|
|||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
|
@ -4,6 +4,7 @@
|
|||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
|
@ -19,6 +20,8 @@
|
|||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
@ -52,7 +55,6 @@ BenchmarkDotNet.Artifacts/
|
|||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
@ -60,7 +62,7 @@ StyleCopReport.xml
|
|||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
|
@ -77,6 +79,7 @@ StyleCopReport.xml
|
|||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
|
@ -208,7 +211,7 @@ _pkginfo.txt
|
|||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
|
@ -221,7 +224,7 @@ ClientBin/
|
|||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
|
@ -252,6 +255,7 @@ ServiceFabricBackup/
|
|||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- Backup*.rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
@ -291,8 +295,8 @@ paket-files/
|
|||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
|
@ -317,7 +321,7 @@ __pycache__/
|
|||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
|
@ -326,5 +330,11 @@ ASALocalRun/
|
|||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
13
.mergify.yml
13
.mergify.yml
|
@ -1,18 +1,18 @@
|
|||
pull_request_rules:
|
||||
- name: automatic strict merge when CI passes, has 2 reviews, no requests for change and is labeled 'ready-to-merge' unless labelled 'do-not-merge/breaking-change' or 'do-not-merge/work-in-progress'
|
||||
- name: automatic strict merge when CI passes, has 1 reviews, no requests for change and is labeled 'ready-to-merge' unless labelled 'do-not-merge/breaking-change' or 'do-not-merge/work-in-progress'
|
||||
conditions:
|
||||
# Only pull-requests sent to the master branch
|
||||
- base=master
|
||||
|
||||
# All Azure builds should be green:
|
||||
- status-success=Uno.UI - CI
|
||||
- status-success=Uno.WinUI3Convert - CI
|
||||
|
||||
# CLA check must pass:
|
||||
#- "status-success=license/cla"
|
||||
|
||||
# Note that this only matches people with write / admin access to the repo,
|
||||
# see <https://doc.mergify.io/conditions.html#attribute-list>
|
||||
- "#approved-reviews-by>=2"
|
||||
- "#approved-reviews-by>=1"
|
||||
- "#changes-requested-reviews-by=0"
|
||||
|
||||
# Pull-request must be labeled with:
|
||||
|
@ -30,10 +30,3 @@ pull_request_rules:
|
|||
# https://doc.mergify.io/strict-workflow.html
|
||||
# https://doc.mergify.io/actions.html#label
|
||||
strict: smart
|
||||
|
||||
- name: automatic merge for allcontributors pull requests
|
||||
conditions:
|
||||
- author=allcontributors[bot]
|
||||
actions:
|
||||
merge:
|
||||
method: merge
|
||||
|
|
31
README.md
31
README.md
|
@ -1,2 +1,29 @@
|
|||
# template
|
||||
template for brand new github repositories
|
||||
# Uno.WinUI3Convert
|
||||
|
||||
Migrate UWP projects to WinUI3/NET5.
|
||||
|
||||
This tool is commonly used in CI environments to automatically generate a WinUI 3 compatible source tree, built separately from the UWP source tree. This allows for the generation of WinUI 3 compatible nuget packages for libraries without having to maintain two separate codebases.
|
||||
|
||||
```
|
||||
Usage:
|
||||
winui3convert [options] <source> <destination>
|
||||
|
||||
Arguments:
|
||||
<source> Source directory
|
||||
<destination> Destination directory
|
||||
|
||||
Options:
|
||||
--overwrite Overwrite destination
|
||||
--version Show version information
|
||||
-?, -h, --help Show help and usage information
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
dotnet tool install --global uno.winui3convert
|
||||
|
||||
## Conversion adjustments
|
||||
|
||||
This tool is meant to help migrate your projects by rewriting namespaces and project files. It won't resolve collisions, work around unsupported features or change code in significant ways.
|
||||
|
||||
Manual source adjustments are to be expected in some cases.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,78 @@
|
|||
// 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.Globalization;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Data.Utilities
|
||||
{
|
||||
internal static class CollectionViewsError
|
||||
{
|
||||
public static class CollectionView
|
||||
{
|
||||
public static InvalidOperationException EnumeratorVersionChanged()
|
||||
{
|
||||
return new InvalidOperationException("Collection was modified; enumeration operation cannot execute.");
|
||||
}
|
||||
|
||||
public static InvalidOperationException MemberNotAllowedDuringAddOrEdit(string paramName)
|
||||
{
|
||||
return new InvalidOperationException(Format("'{0}' is not allowed during an AddNew or EditItem transaction.", paramName));
|
||||
}
|
||||
|
||||
public static InvalidOperationException NoAccessWhileChangesAreDeferred()
|
||||
{
|
||||
return new InvalidOperationException("This value cannot be accessed while changes are deferred.");
|
||||
}
|
||||
|
||||
public static InvalidOperationException ItemNotAtIndex(string paramName)
|
||||
{
|
||||
return new InvalidOperationException(Format("The {0} item is not in the collection.", paramName));
|
||||
}
|
||||
}
|
||||
|
||||
public static class EnumerableCollectionView
|
||||
{
|
||||
public static InvalidOperationException RemovedItemNotFound()
|
||||
{
|
||||
return new InvalidOperationException("The removed item is not found in the source collection.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class ListCollectionView
|
||||
{
|
||||
public static InvalidOperationException CollectionChangedOutOfRange()
|
||||
{
|
||||
return new InvalidOperationException("The collection change is out of bounds of the original collection.");
|
||||
}
|
||||
|
||||
public static InvalidOperationException AddedItemNotInCollection()
|
||||
{
|
||||
return new InvalidOperationException("The added item is not in the collection.");
|
||||
}
|
||||
|
||||
#if FEATURE_IEDITABLECOLLECTIONVIEW
|
||||
public static InvalidOperationException CancelEditNotSupported()
|
||||
{
|
||||
return new InvalidOperationException("CancelEdit is not supported for the current edit item.");
|
||||
}
|
||||
|
||||
public static InvalidOperationException MemberNotAllowedDuringTransaction(string paramName1, string paramName2)
|
||||
{
|
||||
return new InvalidOperationException(Format("'{0}' is not allowed during a transaction started by '{1}'.", paramName1, paramName2));
|
||||
}
|
||||
|
||||
public static InvalidOperationException MemberNotAllowedForView(string paramName)
|
||||
{
|
||||
return new InvalidOperationException(Format("'{0}' is not allowed for this view.", paramName));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string Format(string formatString, params object[] args)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, formatString, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,697 @@
|
|||
// 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;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Data.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection view implementation that supports an IEnumerable source.
|
||||
/// </summary>
|
||||
internal class EnumerableCollectionView : CollectionView
|
||||
{
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Constructors
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
// Set up a ListCollectionView over the snapshot.
|
||||
// We will delegate all CollectionView functionality to this view.
|
||||
internal EnumerableCollectionView(IEnumerable source)
|
||||
: base(source)
|
||||
{
|
||||
_snapshot = new ObservableCollection<object>();
|
||||
|
||||
LoadSnapshotCore(source);
|
||||
|
||||
if (_snapshot.Count > 0)
|
||||
{
|
||||
SetCurrent(_snapshot[0], 0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCurrent(null, -1, 0);
|
||||
}
|
||||
|
||||
// If the source doesn't raise collection change events, try to detect changes by polling the enumerator.
|
||||
_pollForChanges = !(source is INotifyCollectionChanged);
|
||||
|
||||
_view = new ListCollectionView(_snapshot);
|
||||
|
||||
INotifyCollectionChanged incc = _view as INotifyCollectionChanged;
|
||||
incc.CollectionChanged += new NotifyCollectionChangedEventHandler(EnumerableCollectionView_OnViewChanged);
|
||||
|
||||
INotifyPropertyChanged ipc = _view as INotifyPropertyChanged;
|
||||
ipc.PropertyChanged += new PropertyChangedEventHandler(EnumerableCollectionView_OnPropertyChanged);
|
||||
|
||||
_view.CurrentChanging += new CurrentChangingEventHandler(EnumerableCollectionView_OnCurrentChanging);
|
||||
_view.CurrentChanged += new EventHandler<object>(EnumerableCollectionView_OnCurrentChanged);
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Interfaces
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets culture to use during sorting.
|
||||
/// </summary>
|
||||
public override CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.Culture;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_view.Culture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the item belongs to this view. No assumptions are
|
||||
/// made about the item. This method will behave similarly to IList.Contains().
|
||||
/// If the caller knows that the item belongs to the
|
||||
/// underlying collection, it is more efficient to call PassesFilter.
|
||||
/// </summary>
|
||||
/// <returns>True if the item belongs to this view.</returns>
|
||||
public override bool Contains(object item)
|
||||
{
|
||||
EnsureSnapshot();
|
||||
return _view.Contains(item);
|
||||
}
|
||||
|
||||
#if FEATURE_ICOLLECTIONVIEW_FILTER
|
||||
/// <summary>
|
||||
/// Set/get a filter callback to filter out items in collection.
|
||||
/// This property will always accept a filter, but the collection view for the
|
||||
/// underlying InnerList or ItemsSource may not actually support filtering.
|
||||
/// Please check <seealso cref="CanFilter"/>
|
||||
/// </summary>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// Collections assigned to ItemsSource may not support filtering and could throw a NotSupportedException.
|
||||
/// Use <seealso cref="CanSort"/> property to test if sorting is supported before adding
|
||||
/// to SortDescriptions.
|
||||
/// </exception>
|
||||
public override Predicate<object> Filter
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.Filter;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_view.Filter = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if this ICollectionView supports filtering before assigning
|
||||
/// a filter callback to <seealso cref="Filter"/>.
|
||||
/// </summary>
|
||||
public override bool CanFilter
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.CanFilter;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FEATURE_ICOLLECTIONVIEW_SORT
|
||||
/// <summary>
|
||||
/// Set/get Sort criteria to sort items in collection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <p>
|
||||
/// Clear a sort criteria by assigning SortDescription.Empty to this property.
|
||||
/// One or more sort criteria in form of <seealso cref="SortDescription"/>
|
||||
/// can be used, each specifying a property and direction to sort by.
|
||||
/// </p>
|
||||
/// </remarks>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// Simpler implementations do not support sorting and will throw a NotSupportedException.
|
||||
/// Use <seealso cref="CanSort"/> property to test if sorting is supported before adding
|
||||
/// to SortDescriptions.
|
||||
/// </exception>
|
||||
public override SortDescriptionCollection SortDescriptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.SortDescriptions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if this ICollectionView supports sorting before adding
|
||||
/// to <seealso cref="SortDescriptions"/>.
|
||||
/// </summary>
|
||||
public override bool CanSort
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.CanSort;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FEATURE_ICOLLECTIONVIEW_GROUP
|
||||
/// <summary>
|
||||
/// Returns true if this view really supports grouping.
|
||||
/// When this returns false, the rest of the interface is ignored.
|
||||
/// </summary>
|
||||
public override bool CanGroup
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.CanGroup;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The description of grouping, indexed by level.
|
||||
/// </summary>
|
||||
public override ObservableCollection<GroupDescription> GroupDescriptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.GroupDescriptions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The top-level groups, constructed according to the descriptions
|
||||
/// given in GroupDescriptions.
|
||||
/// </summary>
|
||||
public override ReadOnlyObservableCollection<object> Groups
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.Groups;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Enter a Defer Cycle.
|
||||
/// Defer cycles are used to coalesce changes to the ICollectionView.
|
||||
/// </summary>
|
||||
/// <returns>An IDisposable deferral object.</returns>
|
||||
public override IDisposable DeferRefresh()
|
||||
{
|
||||
return _view.DeferRefresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current item.
|
||||
/// </summary>
|
||||
public override object CurrentItem
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.CurrentItem;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ordinal position of the <seealso cref="CurrentItem"/> within the (optionally
|
||||
/// sorted and filtered) view.
|
||||
/// </summary>
|
||||
public override int CurrentPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.CurrentPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the currency is beyond the end (End-Of-File).
|
||||
/// </summary>
|
||||
public override bool IsCurrentAfterLast
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.IsCurrentAfterLast;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the currency is before the beginning (Beginning-Of-File).
|
||||
/// </summary>
|
||||
public override bool IsCurrentBeforeFirst
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.IsCurrentBeforeFirst;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move <seealso cref="CurrentItem"/> to the first item.
|
||||
/// </summary>
|
||||
/// <returns>True if <seealso cref="CurrentItem"/> points to an item within the view.</returns>
|
||||
public override bool MoveCurrentToFirst()
|
||||
{
|
||||
return _view.MoveCurrentToFirst();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move <seealso cref="CurrentItem"/> to the previous item.
|
||||
/// </summary>
|
||||
/// <returns>True if <seealso cref="CurrentItem"/> points to an item within the view.</returns>
|
||||
public override bool MoveCurrentToPrevious()
|
||||
{
|
||||
return _view.MoveCurrentToPrevious();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move <seealso cref="CurrentItem"/> to the next item.
|
||||
/// </summary>
|
||||
/// <returns>True if <seealso cref="CurrentItem"/> points to an item within the view.</returns>
|
||||
public override bool MoveCurrentToNext()
|
||||
{
|
||||
return _view.MoveCurrentToNext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move <seealso cref="CurrentItem"/> to the last item.
|
||||
/// </summary>
|
||||
/// <returns>True if <seealso cref="CurrentItem"/> points to an item within the view.</returns>
|
||||
public override bool MoveCurrentToLast()
|
||||
{
|
||||
return _view.MoveCurrentToLast();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move <seealso cref="CurrentItem"/> to the given item.
|
||||
/// If the item is not found, move to BeforeFirst.
|
||||
/// </summary>
|
||||
/// <param name="item">Move CurrentItem to this item.</param>
|
||||
/// <returns>True if <seealso cref="CurrentItem"/> points to an item within the view.</returns>
|
||||
public override bool MoveCurrentTo(object item)
|
||||
{
|
||||
return _view.MoveCurrentTo(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move <seealso cref="CurrentItem"/> to the item at the given index.
|
||||
/// </summary>
|
||||
/// <param name="position">Move CurrentItem to this index</param>
|
||||
/// <returns>True if <seealso cref="CurrentItem"/> points to an item within the view.</returns>
|
||||
public override bool MoveCurrentToPosition(int position)
|
||||
{
|
||||
// If the index is out of range here, I'll let the
|
||||
// _view be the one to make that determination.
|
||||
return _view.MoveCurrentToPosition(position);
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Public Properties
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of records (or -1, meaning "don't know").
|
||||
/// A virtualizing view should return the best estimate it can
|
||||
/// without de-virtualizing all the data. A non-virtualizing view
|
||||
/// should return the exact count of its (filtered) data.
|
||||
/// </summary>
|
||||
public override int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureSnapshot();
|
||||
return _view.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureSnapshot();
|
||||
return (_view != null) ? _view.IsEmpty : true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this view needs to be refreshed.
|
||||
/// </summary>
|
||||
public override bool NeedsRefresh
|
||||
{
|
||||
get
|
||||
{
|
||||
return _view.NeedsRefresh;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Public Methods
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Return the index where the given item appears, or -1 if doesn't appear.
|
||||
/// </summary>
|
||||
/// <param name="item">data item</param>
|
||||
/// <returns>The index where the given item belongs, or -1 if this index is unknown.</returns>
|
||||
public override int IndexOf(object item)
|
||||
{
|
||||
EnsureSnapshot();
|
||||
return _view.IndexOf(item);
|
||||
}
|
||||
|
||||
#if FEATURE_ICOLLECTIONVIEW_FILTER
|
||||
/// <summary>
|
||||
/// Return true if the item belongs to this view. The item is assumed to belong to the
|
||||
/// underlying DataCollection; this method merely takes filters into account.
|
||||
/// It is commonly used during collection-changed notifications to determine if the added/removed
|
||||
/// item requires processing.
|
||||
/// Returns true if no filter is set on collection view.
|
||||
/// </summary>
|
||||
/// <returns>True if the item belongs to this view.</returns>
|
||||
public override bool PassesFilter(object item)
|
||||
{
|
||||
if (_view.CanFilter && _view.Filter != null)
|
||||
{
|
||||
return _view.Filter(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve item at the given zero-based index in this CollectionView.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown if index is out of range
|
||||
/// </exception>
|
||||
/// <returns>Item at the given zero-based index in this CollectionView.</returns>
|
||||
public override object GetItemAt(int index)
|
||||
{
|
||||
EnsureSnapshot();
|
||||
return _view.GetItemAt(index);
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Protected Methods
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
/// <summary> Implementation of IEnumerable.GetEnumerator().
|
||||
/// This provides a way to enumerate the members of the collection
|
||||
/// without changing the currency.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerator object that enumerates the items in this view.</returns>
|
||||
protected override IEnumerator GetEnumerator()
|
||||
{
|
||||
EnsureSnapshot();
|
||||
return (_view as IEnumerable).GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-create the view over the associated IList
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Any sorting and filtering will take effect during Refresh.
|
||||
/// </remarks>
|
||||
protected override void RefreshOverride()
|
||||
{
|
||||
LoadSnapshot(SourceCollection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a single collection change on the UI thread.
|
||||
/// </summary>
|
||||
/// <param name="args">
|
||||
/// The NotifyCollectionChangedEventArgs to be processed.
|
||||
/// </param>
|
||||
protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
// Apply the change to the snapshot
|
||||
switch (args.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
if (args.NewStartingIndex < 0 || _snapshot.Count <= args.NewStartingIndex)
|
||||
{ // Append
|
||||
for (int i = 0; i < args.NewItems.Count; ++i)
|
||||
{
|
||||
_snapshot.Add(args.NewItems[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Insert
|
||||
for (int i = args.NewItems.Count - 1; i >= 0; --i)
|
||||
{
|
||||
_snapshot.Insert(args.NewStartingIndex, args.NewItems[i]);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
if (args.OldStartingIndex < 0)
|
||||
{
|
||||
throw CollectionViewsError.EnumerableCollectionView.RemovedItemNotFound();
|
||||
}
|
||||
|
||||
for (int i = args.OldItems.Count - 1, index = args.OldStartingIndex + i; i >= 0; --i, --index)
|
||||
{
|
||||
if (!object.Equals(args.OldItems[i], _snapshot[index]))
|
||||
{
|
||||
throw CollectionViewsError.CollectionView.ItemNotAtIndex("removed");
|
||||
}
|
||||
|
||||
_snapshot.RemoveAt(index);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
for (int i = args.NewItems.Count - 1, index = args.NewStartingIndex + i; i >= 0; --i, --index)
|
||||
{
|
||||
if (!object.Equals(args.OldItems[i], _snapshot[index]))
|
||||
{
|
||||
throw CollectionViewsError.CollectionView.ItemNotAtIndex("replaced");
|
||||
}
|
||||
|
||||
_snapshot[index] = args.NewItems[i];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
LoadSnapshot(SourceCollection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Private Methods
|
||||
//
|
||||
//------------------------------------------------------
|
||||
|
||||
// Load a snapshot of the contents of the IEnumerable into the ObservableCollection.
|
||||
private void LoadSnapshot(IEnumerable source)
|
||||
{
|
||||
// Force currency off the collection (gives user a chance to save dirty information).
|
||||
OnCurrentChanging();
|
||||
|
||||
// Remember the values of the scalar properties, so that we can restore
|
||||
// them and raise events after reloading the data
|
||||
object oldCurrentItem = CurrentItem;
|
||||
int oldCurrentPosition = CurrentPosition;
|
||||
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
|
||||
bool oldIsCurrentAfterLast = IsCurrentAfterLast;
|
||||
|
||||
// Reload the data
|
||||
LoadSnapshotCore(source);
|
||||
|
||||
// Tell listeners everything has changed
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
|
||||
OnCurrentChanged();
|
||||
|
||||
if (IsCurrentAfterLast != oldIsCurrentAfterLast)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentAfterLastPropertyName));
|
||||
}
|
||||
|
||||
if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentBeforeFirstPropertyName));
|
||||
}
|
||||
|
||||
if (oldCurrentPosition != CurrentPosition)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(CurrentPositionPropertyName));
|
||||
}
|
||||
|
||||
if (oldCurrentItem != CurrentItem)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(CurrentItemPropertyName));
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadSnapshotCore(IEnumerable source)
|
||||
{
|
||||
_trackingEnumerator = source.GetEnumerator();
|
||||
|
||||
using (IgnoreViewEvents())
|
||||
{
|
||||
_snapshot.Clear();
|
||||
|
||||
while (_trackingEnumerator.MoveNext())
|
||||
{
|
||||
_snapshot.Add(_trackingEnumerator.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the IEnumerable has changed, bring the snapshot up to date.
|
||||
// (This isn't necessary if the IEnumerable is also INotifyCollectionChanged
|
||||
// because we keep the snapshot in sync incrementally.)
|
||||
private void EnsureSnapshot()
|
||||
{
|
||||
if (_pollForChanges)
|
||||
{
|
||||
try
|
||||
{
|
||||
_trackingEnumerator.MoveNext();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// This "feature" is necessarily incomplete (we cannot detect
|
||||
// the changes when they happen, only as a side-effect of some
|
||||
// later operation), and inconsistent (none of the other
|
||||
// collection views does this). Changing a collection without
|
||||
// raising a notification is not supported.
|
||||
|
||||
// Collection was changed - start over with a new enumerator
|
||||
LoadSnapshotCore(SourceCollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IDisposable IgnoreViewEvents()
|
||||
{
|
||||
return new IgnoreViewEventsHelper(this);
|
||||
}
|
||||
|
||||
private void BeginIgnoreEvents()
|
||||
{
|
||||
_ignoreEventsLevel++;
|
||||
}
|
||||
|
||||
private void EndIgnoreEvents()
|
||||
{
|
||||
_ignoreEventsLevel--;
|
||||
}
|
||||
|
||||
// forward events from the internal view to our own listeners
|
||||
private void EnumerableCollectionView_OnPropertyChanged(object sender, PropertyChangedEventArgs args)
|
||||
{
|
||||
if (_ignoreEventsLevel != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if FEATURE_IEDITABLECOLLECTIONVIEW
|
||||
// Also ignore ListCollectionView's property change notifications for
|
||||
// IEditableCollectionView's properties
|
||||
switch (args.PropertyName)
|
||||
{
|
||||
case ListCollectionView.CanAddNewPropertyName:
|
||||
case ListCollectionView.CanCancelEditPropertyName:
|
||||
case ListCollectionView.CanRemovePropertyName:
|
||||
case ListCollectionView.CurrentAddItemPropertyName:
|
||||
case ListCollectionView.CurrentEditItemPropertyName:
|
||||
case ListCollectionView.IsAddingNewPropertyName:
|
||||
case ListCollectionView.IsEditingItemPropertyName:
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
OnPropertyChanged(args);
|
||||
}
|
||||
|
||||
private void EnumerableCollectionView_OnViewChanged(object sender, NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
if (_ignoreEventsLevel != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnCollectionChanged(args);
|
||||
}
|
||||
|
||||
private void EnumerableCollectionView_OnCurrentChanging(object sender, CurrentChangingEventArgs args)
|
||||
{
|
||||
if (_ignoreEventsLevel != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnCurrentChanging(args);
|
||||
}
|
||||
|
||||
private void EnumerableCollectionView_OnCurrentChanged(object sender, object args)
|
||||
{
|
||||
if (_ignoreEventsLevel != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnCurrentChanged();
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
//
|
||||
// Private Fields
|
||||
//
|
||||
//------------------------------------------------------
|
||||
private ListCollectionView _view;
|
||||
private ObservableCollection<object> _snapshot;
|
||||
private IEnumerator _trackingEnumerator;
|
||||
private int _ignoreEventsLevel;
|
||||
private bool _pollForChanges;
|
||||
|
||||
private class IgnoreViewEventsHelper : IDisposable
|
||||
{
|
||||
public IgnoreViewEventsHelper(EnumerableCollectionView parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_parent.BeginIgnoreEvents();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_parent != null)
|
||||
{
|
||||
_parent.EndIgnoreEvents();
|
||||
_parent = null;
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private EnumerableCollectionView _parent;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,988 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Microsoft.Toolkit.Uwp.Utilities;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes <see cref="DataGrid" /> types to UI Automation.
|
||||
/// </summary>
|
||||
public class DataGridAutomationPeer :
|
||||
FrameworkElementAutomationPeer,
|
||||
IGridProvider,
|
||||
IScrollProvider,
|
||||
ISelectionProvider,
|
||||
ITableProvider
|
||||
{
|
||||
private Dictionary<object, DataGridGroupItemAutomationPeer> _groupItemPeers = new Dictionary<object, DataGridGroupItemAutomationPeer>();
|
||||
private Dictionary<object, DataGridItemAutomationPeer> _itemPeers = new Dictionary<object, DataGridItemAutomationPeer>();
|
||||
private bool _oldHorizontallyScrollable;
|
||||
private double _oldHorizontalScrollPercent;
|
||||
private double _oldHorizontalViewSize;
|
||||
private bool _oldVerticallyScrollable;
|
||||
private double _oldVerticalScrollPercent;
|
||||
private double _oldVerticalViewSize;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">
|
||||
/// The <see cref="DataGrid" /> that is associated with this <see cref="T:Windows.UI.Xaml.Automation.Peers.DataGridAutomationPeer" />.
|
||||
/// </param>
|
||||
public DataGridAutomationPeer(DataGrid owner)
|
||||
: base(owner)
|
||||
{
|
||||
if (this.HorizontallyScrollable)
|
||||
{
|
||||
_oldHorizontallyScrollable = true;
|
||||
_oldHorizontalScrollPercent = this.HorizontalScrollPercent;
|
||||
_oldHorizontalViewSize = this.HorizontalViewSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
_oldHorizontallyScrollable = false;
|
||||
_oldHorizontalScrollPercent = ScrollPatternIdentifiers.NoScroll;
|
||||
_oldHorizontalViewSize = 100.0;
|
||||
}
|
||||
|
||||
if (this.VerticallyScrollable)
|
||||
{
|
||||
_oldVerticallyScrollable = true;
|
||||
_oldVerticalScrollPercent = this.VerticalScrollPercent;
|
||||
_oldVerticalViewSize = this.VerticalViewSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
_oldVerticallyScrollable = false;
|
||||
_oldVerticalScrollPercent = ScrollPatternIdentifiers.NoScroll;
|
||||
_oldVerticalViewSize = 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HorizontallyScrollable
|
||||
{
|
||||
get
|
||||
{
|
||||
return OwningDataGrid.HorizontalScrollBar != null && OwningDataGrid.HorizontalScrollBar.Maximum > 0;
|
||||
}
|
||||
}
|
||||
|
||||
private double HorizontalScrollPercent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.HorizontallyScrollable)
|
||||
{
|
||||
return ScrollPatternIdentifiers.NoScroll;
|
||||
}
|
||||
|
||||
return (double)(this.OwningDataGrid.HorizontalScrollBar.Value * 100.0 / this.OwningDataGrid.HorizontalScrollBar.Maximum);
|
||||
}
|
||||
}
|
||||
|
||||
private double HorizontalViewSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.HorizontallyScrollable || DoubleUtil.IsZero(this.OwningDataGrid.HorizontalScrollBar.Maximum))
|
||||
{
|
||||
return 100.0;
|
||||
}
|
||||
|
||||
return (double)(this.OwningDataGrid.HorizontalScrollBar.ViewportSize * 100.0 /
|
||||
(this.OwningDataGrid.HorizontalScrollBar.ViewportSize + this.OwningDataGrid.HorizontalScrollBar.Maximum));
|
||||
}
|
||||
}
|
||||
|
||||
private DataGrid OwningDataGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
return Owner as DataGrid;
|
||||
}
|
||||
}
|
||||
|
||||
private bool VerticallyScrollable
|
||||
{
|
||||
get
|
||||
{
|
||||
return OwningDataGrid.VerticalScrollBar != null && OwningDataGrid.VerticalScrollBar.Maximum > 0;
|
||||
}
|
||||
}
|
||||
|
||||
private double VerticalScrollPercent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.VerticallyScrollable)
|
||||
{
|
||||
return ScrollPatternIdentifiers.NoScroll;
|
||||
}
|
||||
|
||||
return (double)(this.OwningDataGrid.VerticalScrollBar.Value * 100.0 / this.OwningDataGrid.VerticalScrollBar.Maximum);
|
||||
}
|
||||
}
|
||||
|
||||
private double VerticalViewSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!this.VerticallyScrollable || DoubleUtil.IsZero(this.OwningDataGrid.VerticalScrollBar.Maximum))
|
||||
{
|
||||
return 100.0;
|
||||
}
|
||||
|
||||
return (double)(this.OwningDataGrid.VerticalScrollBar.ViewportSize * 100.0 /
|
||||
(this.OwningDataGrid.VerticalScrollBar.ViewportSize + this.OwningDataGrid.VerticalScrollBar.Maximum));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control type for the element that is associated with the UI Automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The control type.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.DataGrid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of elements that are represented in the UI Automation tree as immediate
|
||||
/// child elements of the automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The children elements.</returns>
|
||||
protected override IList<AutomationPeer> GetChildrenCore()
|
||||
{
|
||||
IList<AutomationPeer> children = base.GetChildrenCore();
|
||||
if (this.OwningDataGrid != null)
|
||||
{
|
||||
children.Remove(ScrollBarAutomationPeer.FromElement(this.OwningDataGrid.HorizontalScrollBar));
|
||||
children.Remove(ScrollBarAutomationPeer.FromElement(this.OwningDataGrid.VerticalScrollBar));
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = Owner.GetType().Name;
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine("DataGridAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetName.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Returns the first of these that is not null or empty:
|
||||
/// - Value returned by the base implementation
|
||||
/// - Name of the owning DataGrid
|
||||
/// - DataGrid class name
|
||||
/// </returns>
|
||||
protected override string GetNameCore()
|
||||
{
|
||||
string name = base.GetNameCore();
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
if (this.OwningDataGrid != null)
|
||||
{
|
||||
name = this.OwningDataGrid.Name;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = this.GetClassName();
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine("DataGridAutomationPeer.GetNameCore returns " + name);
|
||||
#endif
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface.
|
||||
/// </summary>
|
||||
/// <param name="patternInterface">A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration.</param>
|
||||
/// <returns>The object that supports the specified pattern, or null if unsupported.</returns>
|
||||
protected override object GetPatternCore(PatternInterface patternInterface)
|
||||
{
|
||||
switch (patternInterface)
|
||||
{
|
||||
case PatternInterface.Grid:
|
||||
case PatternInterface.Selection:
|
||||
case PatternInterface.Table:
|
||||
return this;
|
||||
case PatternInterface.Scroll:
|
||||
{
|
||||
if (this.HorizontallyScrollable || this.VerticallyScrollable)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetPatternCore(patternInterface);
|
||||
}
|
||||
|
||||
int IGridProvider.ColumnCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningDataGrid.Columns.Count;
|
||||
}
|
||||
}
|
||||
|
||||
int IGridProvider.RowCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningDataGrid.DataConnection.Count;
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple IGridProvider.GetItem(int row, int column)
|
||||
{
|
||||
if (this.OwningDataGrid != null &&
|
||||
this.OwningDataGrid.DataConnection != null &&
|
||||
row >= 0 && row < this.OwningDataGrid.SlotCount &&
|
||||
column >= 0 && column < this.OwningDataGrid.Columns.Count)
|
||||
{
|
||||
object item = null;
|
||||
if (!this.OwningDataGrid.IsSlotVisible(this.OwningDataGrid.SlotFromRowIndex(row)))
|
||||
{
|
||||
item = this.OwningDataGrid.DataConnection.GetDataItem(row);
|
||||
}
|
||||
|
||||
this.OwningDataGrid.ScrollIntoView(item, this.OwningDataGrid.Columns[column]);
|
||||
|
||||
DataGridRow dgr = this.OwningDataGrid.DisplayData.GetDisplayedRow(row);
|
||||
if (this.OwningDataGrid.ColumnsInternal.RowGroupSpacerColumn.IsRepresented)
|
||||
{
|
||||
column++;
|
||||
}
|
||||
|
||||
Debug.Assert(column >= 0, "Expected positive column value.");
|
||||
Debug.Assert(column < this.OwningDataGrid.ColumnsItemsInternal.Count, "Expected smaller column value.");
|
||||
DataGridCell cell = dgr.Cells[column];
|
||||
AutomationPeer peer = CreatePeerForElement(cell);
|
||||
if (peer != null)
|
||||
{
|
||||
return ProviderFromPeer(peer);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool IScrollProvider.HorizontallyScrollable
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.HorizontallyScrollable;
|
||||
}
|
||||
}
|
||||
|
||||
double IScrollProvider.HorizontalScrollPercent
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.HorizontalScrollPercent;
|
||||
}
|
||||
}
|
||||
|
||||
double IScrollProvider.HorizontalViewSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.HorizontalViewSize;
|
||||
}
|
||||
}
|
||||
|
||||
bool IScrollProvider.VerticallyScrollable
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.VerticallyScrollable;
|
||||
}
|
||||
}
|
||||
|
||||
double IScrollProvider.VerticalScrollPercent
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.VerticalScrollPercent;
|
||||
}
|
||||
}
|
||||
|
||||
double IScrollProvider.VerticalViewSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.VerticalViewSize;
|
||||
}
|
||||
}
|
||||
|
||||
void IScrollProvider.Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount)
|
||||
{
|
||||
if (!IsEnabled())
|
||||
{
|
||||
throw new ElementNotEnabledException();
|
||||
}
|
||||
|
||||
bool scrollHorizontally = horizontalAmount != ScrollAmount.NoAmount;
|
||||
bool scrollVertically = verticalAmount != ScrollAmount.NoAmount;
|
||||
|
||||
if ((scrollHorizontally && !this.HorizontallyScrollable) || (scrollVertically && !this.VerticallyScrollable))
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
|
||||
switch (horizontalAmount)
|
||||
{
|
||||
// In the small increment and decrement calls, ScrollHorizontally will adjust the
|
||||
// ScrollBar.Value itself, so we don't need to do it here
|
||||
case ScrollAmount.SmallIncrement:
|
||||
this.OwningDataGrid.ProcessHorizontalScroll(ScrollEventType.SmallIncrement);
|
||||
break;
|
||||
case ScrollAmount.LargeIncrement:
|
||||
this.OwningDataGrid.HorizontalScrollBar.Value += this.OwningDataGrid.HorizontalScrollBar.LargeChange;
|
||||
this.OwningDataGrid.ProcessHorizontalScroll(ScrollEventType.LargeIncrement);
|
||||
break;
|
||||
case ScrollAmount.SmallDecrement:
|
||||
this.OwningDataGrid.ProcessHorizontalScroll(ScrollEventType.SmallDecrement);
|
||||
break;
|
||||
case ScrollAmount.LargeDecrement:
|
||||
this.OwningDataGrid.HorizontalScrollBar.Value -= this.OwningDataGrid.HorizontalScrollBar.LargeChange;
|
||||
this.OwningDataGrid.ProcessHorizontalScroll(ScrollEventType.LargeDecrement);
|
||||
break;
|
||||
case ScrollAmount.NoAmount:
|
||||
break;
|
||||
default:
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
|
||||
switch (verticalAmount)
|
||||
{
|
||||
// In the small increment and decrement calls, ScrollVertically will adjust the
|
||||
// ScrollBar.Value itself, so we don't need to do it here
|
||||
case ScrollAmount.SmallIncrement:
|
||||
this.OwningDataGrid.ProcessVerticalScroll(ScrollEventType.SmallIncrement);
|
||||
break;
|
||||
case ScrollAmount.LargeIncrement:
|
||||
this.OwningDataGrid.VerticalScrollBar.Value += this.OwningDataGrid.VerticalScrollBar.LargeChange;
|
||||
this.OwningDataGrid.ProcessVerticalScroll(ScrollEventType.LargeIncrement);
|
||||
break;
|
||||
case ScrollAmount.SmallDecrement:
|
||||
this.OwningDataGrid.ProcessVerticalScroll(ScrollEventType.SmallDecrement);
|
||||
break;
|
||||
case ScrollAmount.LargeDecrement:
|
||||
this.OwningDataGrid.VerticalScrollBar.Value -= this.OwningDataGrid.VerticalScrollBar.LargeChange;
|
||||
this.OwningDataGrid.ProcessVerticalScroll(ScrollEventType.LargeDecrement);
|
||||
break;
|
||||
case ScrollAmount.NoAmount:
|
||||
break;
|
||||
default:
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
}
|
||||
|
||||
void IScrollProvider.SetScrollPercent(double horizontalPercent, double verticalPercent)
|
||||
{
|
||||
if (!IsEnabled())
|
||||
{
|
||||
throw new ElementNotEnabledException();
|
||||
}
|
||||
|
||||
bool scrollHorizontally = horizontalPercent != (double)ScrollPatternIdentifiers.NoScroll;
|
||||
bool scrollVertically = verticalPercent != (double)ScrollPatternIdentifiers.NoScroll;
|
||||
|
||||
if ((scrollHorizontally && !this.HorizontallyScrollable) || (scrollVertically && !this.VerticallyScrollable))
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
|
||||
if (scrollHorizontally && (horizontalPercent < 0.0 || horizontalPercent > 100.0))
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
|
||||
if (scrollVertically && (verticalPercent < 0.0 || verticalPercent > 100.0))
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
|
||||
if (scrollHorizontally)
|
||||
{
|
||||
this.OwningDataGrid.HorizontalScrollBar.Value =
|
||||
(double)(this.OwningDataGrid.HorizontalScrollBar.Maximum * (horizontalPercent / 100.0));
|
||||
this.OwningDataGrid.ProcessHorizontalScroll(ScrollEventType.ThumbPosition);
|
||||
}
|
||||
|
||||
if (scrollVertically)
|
||||
{
|
||||
this.OwningDataGrid.VerticalScrollBar.Value =
|
||||
(double)(this.OwningDataGrid.VerticalScrollBar.Maximum * (verticalPercent / 100.0));
|
||||
this.OwningDataGrid.ProcessVerticalScroll(ScrollEventType.ThumbPosition);
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple[] ISelectionProvider.GetSelection()
|
||||
{
|
||||
if (this.OwningDataGrid != null &&
|
||||
this.OwningDataGrid.SelectedItems != null)
|
||||
{
|
||||
List<IRawElementProviderSimple> selectedProviders = new List<IRawElementProviderSimple>();
|
||||
foreach (object item in this.OwningDataGrid.SelectedItems)
|
||||
{
|
||||
DataGridItemAutomationPeer peer = GetOrCreateItemPeer(item);
|
||||
if (peer != null)
|
||||
{
|
||||
selectedProviders.Add(ProviderFromPeer(peer));
|
||||
}
|
||||
}
|
||||
|
||||
return selectedProviders.ToArray();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool ISelectionProvider.CanSelectMultiple
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningDataGrid.SelectionMode == DataGridSelectionMode.Extended;
|
||||
}
|
||||
}
|
||||
|
||||
bool ISelectionProvider.IsSelectionRequired
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RowOrColumnMajor ITableProvider.RowOrColumnMajor
|
||||
{
|
||||
get
|
||||
{
|
||||
return RowOrColumnMajor.RowMajor;
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple[] ITableProvider.GetColumnHeaders()
|
||||
{
|
||||
if (this.OwningDataGrid.AreColumnHeadersVisible)
|
||||
{
|
||||
List<IRawElementProviderSimple> providers = new List<IRawElementProviderSimple>();
|
||||
foreach (DataGridColumn column in this.OwningDataGrid.Columns)
|
||||
{
|
||||
if (column.HeaderCell != null)
|
||||
{
|
||||
AutomationPeer peer = CreatePeerForElement(column.HeaderCell);
|
||||
if (peer != null)
|
||||
{
|
||||
providers.Add(ProviderFromPeer(peer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (providers.Count > 0)
|
||||
{
|
||||
return providers.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
IRawElementProviderSimple[] ITableProvider.GetRowHeaders()
|
||||
{
|
||||
if (this.OwningDataGrid.AreRowHeadersVisible)
|
||||
{
|
||||
List<IRawElementProviderSimple> providers = new List<IRawElementProviderSimple>();
|
||||
foreach (DataGridRow row in this.OwningDataGrid.DisplayData.GetScrollingElements(true /*onlyRows*/))
|
||||
{
|
||||
if (row.HeaderCell != null)
|
||||
{
|
||||
AutomationPeer peer = CreatePeerForElement(row.HeaderCell);
|
||||
if (peer != null)
|
||||
{
|
||||
providers.Add(ProviderFromPeer(peer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (providers.Count > 0)
|
||||
{
|
||||
return providers.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private AutomationPeer GetCellPeer(int slot, int column)
|
||||
{
|
||||
if (slot >= 0 && slot < this.OwningDataGrid.SlotCount &&
|
||||
column >= 0 && column < this.OwningDataGrid.ColumnsItemsInternal.Count &&
|
||||
this.OwningDataGrid.IsSlotVisible(slot))
|
||||
{
|
||||
DataGridRow row = this.OwningDataGrid.DisplayData.GetDisplayedElement(slot) as DataGridRow;
|
||||
if (row != null)
|
||||
{
|
||||
Debug.Assert(column >= 0, "Expected positive column value.");
|
||||
Debug.Assert(column < this.OwningDataGrid.ColumnsItemsInternal.Count, "Expected smaller column value.");
|
||||
DataGridCell cell = row.Cells[column];
|
||||
return CreatePeerForElement(cell);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static void RaiseAutomationInvokeEvent(UIElement element)
|
||||
{
|
||||
if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
|
||||
{
|
||||
AutomationPeer peer = FrameworkElementAutomationPeer.FromElement(element);
|
||||
if (peer != null)
|
||||
{
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine(peer.ToString() + ".RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked)");
|
||||
#endif
|
||||
peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal List<AutomationPeer> GetChildPeers()
|
||||
{
|
||||
List<AutomationPeer> peers = new List<AutomationPeer>();
|
||||
PopulateGroupItemPeers();
|
||||
PopulateItemPeers();
|
||||
if (_groupItemPeers != null && _groupItemPeers.Count > 0)
|
||||
{
|
||||
foreach (object group in this.OwningDataGrid.DataConnection.CollectionView.CollectionGroups /*Groups*/)
|
||||
{
|
||||
peers.Add(_groupItemPeers[group]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (DataGridItemAutomationPeer itemPeer in _itemPeers.Values)
|
||||
{
|
||||
peers.Add(itemPeer);
|
||||
}
|
||||
}
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
internal DataGridGroupItemAutomationPeer GetOrCreateGroupItemPeer(object group)
|
||||
{
|
||||
DataGridGroupItemAutomationPeer peer = null;
|
||||
|
||||
if (group != null)
|
||||
{
|
||||
if (_groupItemPeers.ContainsKey(group))
|
||||
{
|
||||
peer = _groupItemPeers[group];
|
||||
}
|
||||
else
|
||||
{
|
||||
peer = new DataGridGroupItemAutomationPeer(group as ICollectionViewGroup, this.OwningDataGrid);
|
||||
_groupItemPeers.Add(group, peer);
|
||||
}
|
||||
|
||||
DataGridRowGroupHeaderAutomationPeer rghPeer = peer.OwningRowGroupHeaderPeer;
|
||||
if (rghPeer != null)
|
||||
{
|
||||
rghPeer.EventsSource = peer;
|
||||
}
|
||||
}
|
||||
|
||||
return peer;
|
||||
}
|
||||
|
||||
internal DataGridItemAutomationPeer GetOrCreateItemPeer(object item)
|
||||
{
|
||||
DataGridItemAutomationPeer peer = null;
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
if (_itemPeers.ContainsKey(item))
|
||||
{
|
||||
peer = _itemPeers[item];
|
||||
}
|
||||
else
|
||||
{
|
||||
peer = new DataGridItemAutomationPeer(item, this.OwningDataGrid);
|
||||
_itemPeers.Add(item, peer);
|
||||
}
|
||||
|
||||
DataGridRowAutomationPeer rowPeer = peer.OwningRowPeer;
|
||||
if (rowPeer != null)
|
||||
{
|
||||
rowPeer.EventsSource = peer;
|
||||
}
|
||||
}
|
||||
|
||||
return peer;
|
||||
}
|
||||
|
||||
internal void PopulateGroupItemPeers()
|
||||
{
|
||||
Dictionary<object, DataGridGroupItemAutomationPeer> oldChildren = new Dictionary<object, DataGridGroupItemAutomationPeer>(_groupItemPeers);
|
||||
_groupItemPeers.Clear();
|
||||
|
||||
if (this.OwningDataGrid.DataConnection.CollectionView != null &&
|
||||
#if FEATURE_ICOLLECTIONVIEW_GROUP
|
||||
this.OwningDataGrid.DataConnection.CollectionView.CanGroup &&
|
||||
#endif
|
||||
this.OwningDataGrid.DataConnection.CollectionView.CollectionGroups != null &&
|
||||
this.OwningDataGrid.DataConnection.CollectionView.CollectionGroups.Count > 0)
|
||||
{
|
||||
List<object> groups = new List<object>(this.OwningDataGrid.DataConnection.CollectionView.CollectionGroups);
|
||||
while (groups.Count > 0)
|
||||
{
|
||||
ICollectionViewGroup cvGroup = groups[0] as ICollectionViewGroup;
|
||||
groups.RemoveAt(0);
|
||||
if (cvGroup != null)
|
||||
{
|
||||
// Add the group's peer to the collection
|
||||
DataGridGroupItemAutomationPeer peer = null;
|
||||
|
||||
if (oldChildren.ContainsKey(cvGroup))
|
||||
{
|
||||
peer = oldChildren[cvGroup] as DataGridGroupItemAutomationPeer;
|
||||
}
|
||||
else
|
||||
{
|
||||
peer = new DataGridGroupItemAutomationPeer(cvGroup, this.OwningDataGrid);
|
||||
}
|
||||
|
||||
if (peer != null)
|
||||
{
|
||||
DataGridRowGroupHeaderAutomationPeer rghPeer = peer.OwningRowGroupHeaderPeer;
|
||||
if (rghPeer != null)
|
||||
{
|
||||
rghPeer.EventsSource = peer;
|
||||
}
|
||||
}
|
||||
|
||||
// This guards against the addition of duplicate items
|
||||
if (!_groupItemPeers.ContainsKey(cvGroup))
|
||||
{
|
||||
_groupItemPeers.Add(cvGroup, peer);
|
||||
}
|
||||
|
||||
#if FEATURE_ICOLLECTIONVIEW_GROUP
|
||||
// Look for any sub groups
|
||||
if (!cvGroup.IsBottomLevel)
|
||||
{
|
||||
int position = 0;
|
||||
foreach (object subGroup in cvGroup.Items)
|
||||
{
|
||||
groups.Insert(position, subGroup);
|
||||
position++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void PopulateItemPeers()
|
||||
{
|
||||
Dictionary<object, DataGridItemAutomationPeer> oldChildren = new Dictionary<object, DataGridItemAutomationPeer>(_itemPeers);
|
||||
_itemPeers.Clear();
|
||||
|
||||
if (this.OwningDataGrid.ItemsSource != null)
|
||||
{
|
||||
foreach (object item in this.OwningDataGrid.ItemsSource)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
DataGridItemAutomationPeer peer;
|
||||
if (oldChildren.ContainsKey(item))
|
||||
{
|
||||
peer = oldChildren[item] as DataGridItemAutomationPeer;
|
||||
}
|
||||
else
|
||||
{
|
||||
peer = new DataGridItemAutomationPeer(item, this.OwningDataGrid);
|
||||
}
|
||||
|
||||
if (peer != null)
|
||||
{
|
||||
DataGridRowAutomationPeer rowPeer = peer.OwningRowPeer;
|
||||
if (rowPeer != null)
|
||||
{
|
||||
rowPeer.EventsSource = peer;
|
||||
}
|
||||
}
|
||||
|
||||
// This guards against the addition of duplicate items
|
||||
if (!_itemPeers.ContainsKey(item))
|
||||
{
|
||||
_itemPeers.Add(item, peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RaiseAutomationCellSelectedEvent(int slot, int column)
|
||||
{
|
||||
AutomationPeer cellPeer = GetCellPeer(slot, column);
|
||||
if (cellPeer != null)
|
||||
{
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine(cellPeer.ToString() + ".RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementSelected)");
|
||||
#endif
|
||||
cellPeer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementSelected);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RaiseAutomationFocusChangedEvent(int slot, int column)
|
||||
{
|
||||
if (slot >= 0 && slot < this.OwningDataGrid.SlotCount &&
|
||||
column >= 0 && column < this.OwningDataGrid.ColumnsItemsInternal.Count &&
|
||||
this.OwningDataGrid.IsSlotVisible(slot))
|
||||
{
|
||||
if (this.OwningDataGrid.RowGroupHeadersTable.Contains(slot))
|
||||
{
|
||||
DataGridRowGroupHeader header = this.OwningDataGrid.DisplayData.GetDisplayedElement(slot) as DataGridRowGroupHeader;
|
||||
if (header != null)
|
||||
{
|
||||
AutomationPeer headerPeer = CreatePeerForElement(header);
|
||||
if (headerPeer != null)
|
||||
{
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine(headerPeer.ToString() + ".RaiseAutomationEvent(AutomationEvents.AutomationFocusChanged)");
|
||||
#endif
|
||||
headerPeer.RaiseAutomationEvent(AutomationEvents.AutomationFocusChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AutomationPeer cellPeer = GetCellPeer(slot, column);
|
||||
if (cellPeer != null)
|
||||
{
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine(cellPeer.ToString() + ".RaiseAutomationEvent(AutomationEvents.AutomationFocusChanged)");
|
||||
#endif
|
||||
cellPeer.RaiseAutomationEvent(AutomationEvents.AutomationFocusChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RaiseAutomationInvokeEvents(DataGridEditingUnit editingUnit, DataGridColumn column, DataGridRow row)
|
||||
{
|
||||
switch (editingUnit)
|
||||
{
|
||||
case DataGridEditingUnit.Cell:
|
||||
{
|
||||
DataGridCell cell = row.Cells[column.Index];
|
||||
AutomationPeer peer = FromElement(cell);
|
||||
if (peer != null)
|
||||
{
|
||||
peer.InvalidatePeer();
|
||||
}
|
||||
else
|
||||
{
|
||||
peer = CreatePeerForElement(cell);
|
||||
}
|
||||
|
||||
if (peer != null)
|
||||
{
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine(peer.ToString() + ".RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked)");
|
||||
#endif
|
||||
peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DataGridEditingUnit.Row:
|
||||
{
|
||||
DataGridItemAutomationPeer peer = GetOrCreateItemPeer(row.DataContext);
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine("DataGridItemAutomationPeer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked)");
|
||||
#endif
|
||||
peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RaiseAutomationScrollEvents()
|
||||
{
|
||||
IScrollProvider isp = (IScrollProvider)this;
|
||||
|
||||
bool newHorizontallyScrollable = isp.HorizontallyScrollable;
|
||||
double newHorizontalViewSize = isp.HorizontalViewSize;
|
||||
double newHorizontalScrollPercent = isp.HorizontalScrollPercent;
|
||||
|
||||
bool newVerticallyScrollable = isp.VerticallyScrollable;
|
||||
double newVerticalViewSize = isp.VerticalViewSize;
|
||||
double newVerticalScrollPercent = isp.VerticalScrollPercent;
|
||||
|
||||
if (_oldHorizontallyScrollable != newHorizontallyScrollable)
|
||||
{
|
||||
RaisePropertyChangedEvent(
|
||||
ScrollPatternIdentifiers.HorizontallyScrollableProperty,
|
||||
_oldHorizontallyScrollable,
|
||||
newHorizontallyScrollable);
|
||||
_oldHorizontallyScrollable = newHorizontallyScrollable;
|
||||
}
|
||||
|
||||
if (_oldHorizontalViewSize != newHorizontalViewSize)
|
||||
{
|
||||
RaisePropertyChangedEvent(
|
||||
ScrollPatternIdentifiers.HorizontalViewSizeProperty,
|
||||
_oldHorizontalViewSize,
|
||||
newHorizontalViewSize);
|
||||
_oldHorizontalViewSize = newHorizontalViewSize;
|
||||
}
|
||||
|
||||
if (_oldHorizontalScrollPercent != newHorizontalScrollPercent)
|
||||
{
|
||||
RaisePropertyChangedEvent(
|
||||
ScrollPatternIdentifiers.HorizontalScrollPercentProperty,
|
||||
_oldHorizontalScrollPercent,
|
||||
newHorizontalScrollPercent);
|
||||
_oldHorizontalScrollPercent = newHorizontalScrollPercent;
|
||||
}
|
||||
|
||||
if (_oldVerticallyScrollable != newVerticallyScrollable)
|
||||
{
|
||||
RaisePropertyChangedEvent(
|
||||
ScrollPatternIdentifiers.VerticallyScrollableProperty,
|
||||
_oldVerticallyScrollable,
|
||||
newVerticallyScrollable);
|
||||
_oldVerticallyScrollable = newVerticallyScrollable;
|
||||
}
|
||||
|
||||
if (_oldVerticalViewSize != newVerticalViewSize)
|
||||
{
|
||||
RaisePropertyChangedEvent(
|
||||
ScrollPatternIdentifiers.VerticalViewSizeProperty,
|
||||
_oldVerticalViewSize,
|
||||
newVerticalViewSize);
|
||||
_oldVerticalViewSize = newVerticalViewSize;
|
||||
}
|
||||
|
||||
if (_oldVerticalScrollPercent != newVerticalScrollPercent)
|
||||
{
|
||||
RaisePropertyChangedEvent(
|
||||
ScrollPatternIdentifiers.VerticalScrollPercentProperty,
|
||||
_oldVerticalScrollPercent,
|
||||
newVerticalScrollPercent);
|
||||
_oldVerticalScrollPercent = newVerticalScrollPercent;
|
||||
}
|
||||
}
|
||||
|
||||
internal void RaiseAutomationSelectionEvents(SelectionChangedEventArgs e)
|
||||
{
|
||||
// If the result of an AddToSelection or RemoveFromSelection is a single selected item,
|
||||
// then all we raise is the ElementSelectedEvent for single item
|
||||
if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) &&
|
||||
this.OwningDataGrid.SelectedItems.Count == 1)
|
||||
{
|
||||
if (this.OwningDataGrid.SelectedItem != null && _itemPeers.ContainsKey(this.OwningDataGrid.SelectedItem))
|
||||
{
|
||||
DataGridItemAutomationPeer peer = _itemPeers[this.OwningDataGrid.SelectedItem];
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine("DataGridItemAutomationPeer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementSelected)");
|
||||
#endif
|
||||
peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementSelected);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
|
||||
if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection))
|
||||
{
|
||||
for (i = 0; i < e.AddedItems.Count; i++)
|
||||
{
|
||||
if (e.AddedItems[i] != null && _itemPeers.ContainsKey(e.AddedItems[i]))
|
||||
{
|
||||
DataGridItemAutomationPeer peer = _itemPeers[e.AddedItems[i]];
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine("DataGridItemAutomationPeer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementAddedToSelection)");
|
||||
#endif
|
||||
peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementAddedToSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection))
|
||||
{
|
||||
for (i = 0; i < e.RemovedItems.Count; i++)
|
||||
{
|
||||
if (e.RemovedItems[i] != null && _itemPeers.ContainsKey(e.RemovedItems[i]))
|
||||
{
|
||||
DataGridItemAutomationPeer peer = _itemPeers[e.RemovedItems[i]];
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine("DataGridItemAutomationPeer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)");
|
||||
#endif
|
||||
peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateRowGroupHeaderPeerEventsSource(DataGridRowGroupHeader header)
|
||||
{
|
||||
object group = header.RowGroupInfo.CollectionViewGroup;
|
||||
DataGridRowGroupHeaderAutomationPeer peer = DataGridRowGroupHeaderAutomationPeer.FromElement(header) as DataGridRowGroupHeaderAutomationPeer;
|
||||
if (peer != null && group != null && _groupItemPeers.ContainsKey(group))
|
||||
{
|
||||
peer.EventsSource = _groupItemPeers[group];
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateRowPeerEventsSource(DataGridRow row)
|
||||
{
|
||||
DataGridRowAutomationPeer peer = FromElement(row) as DataGridRowAutomationPeer;
|
||||
if (peer != null && row.DataContext != null && _itemPeers.ContainsKey(row.DataContext))
|
||||
{
|
||||
peer.EventsSource = _itemPeers[row.DataContext];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,403 @@
|
|||
// 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 Microsoft.Toolkit.Uwp.UI.Controls;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Windows.UI.Xaml.Automation;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for DataGridCell
|
||||
/// </summary>
|
||||
public class DataGridCellAutomationPeer : FrameworkElementAutomationPeer,
|
||||
IGridItemProvider, IInvokeProvider, IScrollItemProvider, ISelectionItemProvider, ITableItemProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridCellAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">DataGridCell</param>
|
||||
public DataGridCellAutomationPeer(DataGridCell owner)
|
||||
: base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
private IRawElementProviderSimple ContainingGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
AutomationPeer peer = CreatePeerForElement(this.OwningGrid);
|
||||
if (peer != null)
|
||||
{
|
||||
return ProviderFromPeer(peer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridCell OwningCell
|
||||
{
|
||||
get
|
||||
{
|
||||
return Owner as DataGridCell;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridColumn OwningColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningCell.OwningColumn;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGrid OwningGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningCell.OwningGrid;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridRow OwningRow
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningCell.OwningRow;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control type for the element that is associated with the UI Automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The control type.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
if (this.OwningColumn != null)
|
||||
{
|
||||
if (this.OwningColumn is DataGridCheckBoxColumn)
|
||||
{
|
||||
return AutomationControlType.CheckBox;
|
||||
}
|
||||
|
||||
if (this.OwningColumn is DataGridTextColumn)
|
||||
{
|
||||
return AutomationControlType.Text;
|
||||
}
|
||||
|
||||
if (this.OwningColumn is DataGridComboBoxColumn)
|
||||
{
|
||||
return AutomationControlType.ComboBox;
|
||||
}
|
||||
}
|
||||
|
||||
return AutomationControlType.Custom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = Owner.GetType().Name;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridCellAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the element.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetNameCore()
|
||||
{
|
||||
TextBlock textBlock = this.OwningCell.Content as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
return textBlock.Text;
|
||||
}
|
||||
|
||||
TextBox textBox = this.OwningCell.Content as TextBox;
|
||||
if (textBox != null)
|
||||
{
|
||||
return textBox.Text;
|
||||
}
|
||||
|
||||
if (this.OwningColumn != null && this.OwningRow != null)
|
||||
{
|
||||
object cellContent = null;
|
||||
DataGridBoundColumn boundColumn = this.OwningColumn as DataGridBoundColumn;
|
||||
if (boundColumn != null && boundColumn.Binding != null)
|
||||
{
|
||||
cellContent = boundColumn.GetCellValue(this.OwningRow.DataContext, boundColumn.Binding);
|
||||
}
|
||||
|
||||
if (cellContent == null && this.OwningColumn.ClipboardContentBinding != null)
|
||||
{
|
||||
cellContent = this.OwningColumn.GetCellValue(this.OwningRow.DataContext, this.OwningColumn.ClipboardContentBinding);
|
||||
}
|
||||
|
||||
if (cellContent != null)
|
||||
{
|
||||
string cellName = cellContent.ToString();
|
||||
if (!string.IsNullOrEmpty(cellName))
|
||||
{
|
||||
return cellName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetNameCore();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface.
|
||||
/// </summary>
|
||||
/// <param name="patternInterface">A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration.</param>
|
||||
/// <returns>The object that supports the specified pattern, or null if unsupported.</returns>
|
||||
protected override object GetPatternCore(PatternInterface patternInterface)
|
||||
{
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
switch (patternInterface)
|
||||
{
|
||||
case PatternInterface.Invoke:
|
||||
{
|
||||
if (!this.OwningGrid.IsReadOnly &&
|
||||
this.OwningColumn != null &&
|
||||
!this.OwningColumn.IsReadOnly)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PatternInterface.ScrollItem:
|
||||
{
|
||||
if (this.OwningGrid.HorizontalScrollBar != null &&
|
||||
this.OwningGrid.HorizontalScrollBar.Maximum > 0)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PatternInterface.GridItem:
|
||||
case PatternInterface.SelectionItem:
|
||||
case PatternInterface.TableItem:
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetPatternCore(patternInterface);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether the element can accept keyboard focus.
|
||||
/// </summary>
|
||||
/// <returns>true if the element can accept keyboard focus; otherwise, false</returns>
|
||||
protected override bool IsKeyboardFocusableCore()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int IGridItemProvider.Column
|
||||
{
|
||||
get
|
||||
{
|
||||
int column = this.OwningCell.ColumnIndex;
|
||||
if (column >= 0 && this.OwningGrid != null && this.OwningGrid.ColumnsInternal.RowGroupSpacerColumn.IsRepresented)
|
||||
{
|
||||
column--;
|
||||
}
|
||||
|
||||
return column;
|
||||
}
|
||||
}
|
||||
|
||||
int IGridItemProvider.ColumnSpan
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple IGridItemProvider.ContainingGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ContainingGrid;
|
||||
}
|
||||
}
|
||||
|
||||
int IGridItemProvider.Row
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningCell.RowIndex;
|
||||
}
|
||||
}
|
||||
|
||||
int IGridItemProvider.RowSpan
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void IInvokeProvider.Invoke()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
if (this.OwningGrid.WaitForLostFocus(() => { ((IInvokeProvider)this).Invoke(); }))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.OwningGrid.EditingRow == this.OwningRow && this.OwningGrid.EditingColumnIndex == this.OwningCell.ColumnIndex)
|
||||
{
|
||||
this.OwningGrid.CommitEdit(DataGridEditingUnit.Cell, true /*exitEditingMode*/);
|
||||
}
|
||||
else if (this.OwningGrid.UpdateSelectionAndCurrency(this.OwningCell.ColumnIndex, this.OwningRow.Slot, DataGridSelectionAction.SelectCurrent, true))
|
||||
{
|
||||
this.OwningGrid.BeginEdit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IScrollItemProvider.ScrollIntoView()
|
||||
{
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
this.OwningGrid.ScrollIntoView(this.OwningCell.DataContext, this.OwningColumn);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
}
|
||||
|
||||
bool ISelectionItemProvider.IsSelected
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningGrid != null && this.OwningRow != null)
|
||||
{
|
||||
return this.OwningRow.IsSelected;
|
||||
}
|
||||
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple ISelectionItemProvider.SelectionContainer
|
||||
{
|
||||
get
|
||||
{
|
||||
AutomationPeer peer = CreatePeerForElement(this.OwningRow);
|
||||
if (peer != null)
|
||||
{
|
||||
return ProviderFromPeer(peer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void ISelectionItemProvider.AddToSelection()
|
||||
{
|
||||
EnsureEnabled();
|
||||
if (this.OwningCell.OwningGrid == null ||
|
||||
this.OwningCell.OwningGrid.CurrentSlot != this.OwningCell.RowIndex ||
|
||||
this.OwningCell.OwningGrid.CurrentColumnIndex != this.OwningCell.ColumnIndex)
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
}
|
||||
|
||||
void ISelectionItemProvider.RemoveFromSelection()
|
||||
{
|
||||
EnsureEnabled();
|
||||
if (this.OwningCell.OwningGrid == null ||
|
||||
(this.OwningCell.OwningGrid.CurrentSlot == this.OwningCell.RowIndex &&
|
||||
this.OwningCell.OwningGrid.CurrentColumnIndex == this.OwningCell.ColumnIndex))
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
}
|
||||
|
||||
void ISelectionItemProvider.Select()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
if (this.OwningGrid.WaitForLostFocus(() => { ((ISelectionItemProvider)this).Select(); }))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.OwningGrid.UpdateSelectionAndCurrency(this.OwningCell.ColumnIndex, this.OwningRow.Slot, DataGridSelectionAction.SelectCurrent, false);
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple[] ITableItemProvider.GetColumnHeaderItems()
|
||||
{
|
||||
if (this.OwningGrid != null &&
|
||||
this.OwningGrid.AreColumnHeadersVisible &&
|
||||
this.OwningColumn.HeaderCell != null)
|
||||
{
|
||||
AutomationPeer peer = CreatePeerForElement(this.OwningColumn.HeaderCell);
|
||||
if (peer != null)
|
||||
{
|
||||
List<IRawElementProviderSimple> providers = new List<IRawElementProviderSimple>(1);
|
||||
providers.Add(ProviderFromPeer(peer));
|
||||
return providers.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
IRawElementProviderSimple[] ITableItemProvider.GetRowHeaderItems()
|
||||
{
|
||||
if (this.OwningGrid != null &&
|
||||
this.OwningGrid.AreRowHeadersVisible &&
|
||||
this.OwningRow.HeaderCell != null)
|
||||
{
|
||||
AutomationPeer peer = CreatePeerForElement(this.OwningRow.HeaderCell);
|
||||
if (peer != null)
|
||||
{
|
||||
List<IRawElementProviderSimple> providers = new List<IRawElementProviderSimple>(1);
|
||||
providers.Add(ProviderFromPeer(peer));
|
||||
return providers.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void EnsureEnabled()
|
||||
{
|
||||
if (!IsEnabled())
|
||||
{
|
||||
throw new ElementNotEnabledException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
// 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.ComponentModel;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for DataGridColumnHeader
|
||||
/// </summary>
|
||||
public class DataGridColumnHeaderAutomationPeer : FrameworkElementAutomationPeer,
|
||||
IInvokeProvider, IScrollItemProvider, ITransformProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridColumnHeaderAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">DataGridColumnHeader</param>
|
||||
public DataGridColumnHeaderAutomationPeer(DataGridColumnHeader owner)
|
||||
: base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
private DataGridColumnHeader OwningHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return Owner as DataGridColumnHeader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control type for the element that is associated with the UI Automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The control type.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.HeaderItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = Owner.GetType().Name;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridColumnHeaderAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the string that describes the functionality of the control that is associated with the automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the help text.</returns>
|
||||
protected override string GetHelpTextCore()
|
||||
{
|
||||
if (this.OwningHeader.OwningColumn != null && this.OwningHeader.OwningColumn.SortDirection.HasValue)
|
||||
{
|
||||
if (this.OwningHeader.OwningColumn.SortDirection.Value == DataGridSortDirection.Ascending)
|
||||
{
|
||||
return "Ascending";
|
||||
}
|
||||
|
||||
return "Descending";
|
||||
}
|
||||
|
||||
return base.GetHelpTextCore();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the element.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetNameCore()
|
||||
{
|
||||
string header = this.OwningHeader.Content as string;
|
||||
if (header != null)
|
||||
{
|
||||
return header;
|
||||
}
|
||||
|
||||
return base.GetNameCore();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface.
|
||||
/// </summary>
|
||||
/// <param name="patternInterface">A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration.</param>
|
||||
/// <returns>The object that supports the specified pattern, or null if unsupported.</returns>
|
||||
protected override object GetPatternCore(PatternInterface patternInterface)
|
||||
{
|
||||
if (this.OwningHeader.OwningGrid != null)
|
||||
{
|
||||
switch (patternInterface)
|
||||
{
|
||||
case PatternInterface.Invoke:
|
||||
// this.OwningHeader.OwningGrid.DataConnection.AllowSort property is ignored because of the DataGrid.Sorting custom sorting capability.
|
||||
if (this.OwningHeader.OwningGrid.CanUserSortColumns &&
|
||||
this.OwningHeader.OwningColumn.CanUserSort)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PatternInterface.ScrollItem:
|
||||
if (this.OwningHeader.OwningGrid.HorizontalScrollBar != null &&
|
||||
this.OwningHeader.OwningGrid.HorizontalScrollBar.Maximum > 0)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PatternInterface.Transform:
|
||||
if (this.OwningHeader.OwningColumn != null &&
|
||||
this.OwningHeader.OwningColumn.ActualCanUserResize)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetPatternCore(patternInterface);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that specifies whether the element is a content element.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is a content element; otherwise false</returns>
|
||||
protected override bool IsContentElementCore()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void IInvokeProvider.Invoke()
|
||||
{
|
||||
this.OwningHeader.InvokeProcessSort();
|
||||
}
|
||||
|
||||
void IScrollItemProvider.ScrollIntoView()
|
||||
{
|
||||
this.OwningHeader.OwningGrid.ScrollIntoView(null, this.OwningHeader.OwningColumn);
|
||||
}
|
||||
|
||||
bool ITransformProvider.CanMove
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ITransformProvider.CanResize
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningHeader.OwningColumn != null && this.OwningHeader.OwningColumn.ActualCanUserResize;
|
||||
}
|
||||
}
|
||||
|
||||
bool ITransformProvider.CanRotate
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ITransformProvider.Move(double x, double y)
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
|
||||
void ITransformProvider.Resize(double width, double height)
|
||||
{
|
||||
if (this.OwningHeader.OwningColumn != null &&
|
||||
this.OwningHeader.OwningColumn.ActualCanUserResize)
|
||||
{
|
||||
this.OwningHeader.OwningColumn.Width = new DataGridLength(width);
|
||||
}
|
||||
}
|
||||
|
||||
void ITransformProvider.Rotate(double degrees)
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// 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.Primitives;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for DataGridColumnHeadersPresenter
|
||||
/// </summary>
|
||||
public class DataGridColumnHeadersPresenterAutomationPeer : FrameworkElementAutomationPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridColumnHeadersPresenterAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">DataGridColumnHeadersPresenter</param>
|
||||
public DataGridColumnHeadersPresenterAutomationPeer(DataGridColumnHeadersPresenter owner)
|
||||
: base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control type for the element that is associated with the UI Automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The control type.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.Header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = Owner.GetType().Name;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridColumnHeadersPresenterAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that specifies whether the element is a content element.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is a content element; otherwise false</returns>
|
||||
protected override bool IsContentElementCore()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// 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.Primitives;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for DataGridDetailsPresenter
|
||||
/// </summary>
|
||||
public class DataGridDetailsPresenterAutomationPeer : FrameworkElementAutomationPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridDetailsPresenterAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">DataGridDetailsPresenter</param>
|
||||
public DataGridDetailsPresenterAutomationPeer(DataGridDetailsPresenter owner)
|
||||
: base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control type for the DataGridDetailsPresenter element that is associated with the UI Automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The control type.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.Custom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = Owner.GetType().Name;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridDetailsPresenterAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the DataGridDetailsPresenter associated with this UIElementAutomationPeer
|
||||
/// is understood by the end user as interactive.
|
||||
/// </summary>
|
||||
/// <returns>True if the DataGridDetailsPresenter associated with this UIElementAutomationPeer
|
||||
/// is understood by the end user as interactive.</returns>
|
||||
protected override bool IsControlElementCore()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that specifies whether the element is a content element.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is a content element; otherwise false</returns>
|
||||
protected override bool IsContentElementCore()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,575 @@
|
|||
// 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 Microsoft.Toolkit.Uwp.UI.Controls;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for a group of items in a DataGrid
|
||||
/// </summary>
|
||||
public class DataGridGroupItemAutomationPeer : FrameworkElementAutomationPeer,
|
||||
IExpandCollapseProvider, IGridProvider, IScrollItemProvider, ISelectionProvider
|
||||
{
|
||||
private ICollectionViewGroup _group;
|
||||
private AutomationPeer _dataGridAutomationPeer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridGroupItemAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
public DataGridGroupItemAutomationPeer(ICollectionViewGroup group, DataGrid dataGrid)
|
||||
: base(dataGrid)
|
||||
{
|
||||
if (group == null)
|
||||
{
|
||||
throw new ArgumentNullException("group");
|
||||
}
|
||||
|
||||
if (dataGrid == null)
|
||||
{
|
||||
throw new ArgumentNullException("dataGrid");
|
||||
}
|
||||
|
||||
_group = group;
|
||||
_dataGridAutomationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(dataGrid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the owning DataGrid
|
||||
/// </summary>
|
||||
private DataGrid OwningDataGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
DataGridAutomationPeer gridPeer = _dataGridAutomationPeer as DataGridAutomationPeer;
|
||||
return gridPeer.Owner as DataGrid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the owning DataGrid's Automation Peer
|
||||
/// </summary>
|
||||
private DataGridAutomationPeer OwningDataGridPeer
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dataGridAutomationPeer as DataGridAutomationPeer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the owning DataGridRowGroupHeader
|
||||
/// </summary>
|
||||
private DataGridRowGroupHeader OwningRowGroupHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningDataGrid != null)
|
||||
{
|
||||
DataGridRowGroupInfo groupInfo = this.OwningDataGrid.RowGroupInfoFromCollectionViewGroup(_group);
|
||||
if (groupInfo != null && this.OwningDataGrid.IsSlotVisible(groupInfo.Slot))
|
||||
{
|
||||
return this.OwningDataGrid.DisplayData.GetDisplayedElement(groupInfo.Slot) as DataGridRowGroupHeader;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the owning DataGridRowGroupHeader's Automation Peer
|
||||
/// </summary>
|
||||
internal DataGridRowGroupHeaderAutomationPeer OwningRowGroupHeaderPeer
|
||||
{
|
||||
get
|
||||
{
|
||||
DataGridRowGroupHeaderAutomationPeer rowGroupHeaderPeer = null;
|
||||
DataGridRowGroupHeader rowGroupHeader = this.OwningRowGroupHeader;
|
||||
if (rowGroupHeader != null)
|
||||
{
|
||||
rowGroupHeaderPeer = FrameworkElementAutomationPeer.FromElement(rowGroupHeader) as DataGridRowGroupHeaderAutomationPeer;
|
||||
if (rowGroupHeaderPeer == null)
|
||||
{
|
||||
rowGroupHeaderPeer = FrameworkElementAutomationPeer.CreatePeerForElement(rowGroupHeader) as DataGridRowGroupHeaderAutomationPeer;
|
||||
}
|
||||
}
|
||||
|
||||
return rowGroupHeaderPeer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the accelerator key for the UIElement that is associated with this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The accelerator key for the UIElement that is associated with this DataGridGroupItemAutomationPeer.</returns>
|
||||
protected override string GetAcceleratorKeyCore()
|
||||
{
|
||||
return (this.OwningRowGroupHeaderPeer != null) ? this.OwningRowGroupHeaderPeer.GetAcceleratorKey() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the access key for the UIElement that is associated with this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The access key for the UIElement that is associated with this DataGridGroupItemAutomationPeer.</returns>
|
||||
protected override string GetAccessKeyCore()
|
||||
{
|
||||
return (this.OwningRowGroupHeaderPeer != null) ? this.OwningRowGroupHeaderPeer.GetAccessKey() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the control type for the UIElement that is associated with this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The control type for the UIElement that is associated with this DataGridGroupItemAutomationPeer.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.Group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string that uniquely identifies the FrameworkElement that is associated with this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that uniquely identifies the FrameworkElement that is associated with this DataGridGroupItemAutomationPeer.</returns>
|
||||
protected override string GetAutomationIdCore()
|
||||
{
|
||||
// The AutomationId should be unset for dynamic content.
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Rect that represents the bounding rectangle of the UIElement that is associated with this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The Rect that represents the bounding rectangle of the UIElement that is associated with this DataGridGroupItemAutomationPeer.</returns>
|
||||
protected override Rect GetBoundingRectangleCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.GetBoundingRectangle() : default(Rect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the collection of elements that are represented in the UI Automation tree as immediate
|
||||
/// child elements of the automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The children elements.</returns>
|
||||
protected override IList<AutomationPeer> GetChildrenCore()
|
||||
{
|
||||
List<AutomationPeer> children = new List<AutomationPeer>();
|
||||
if (this.OwningRowGroupHeaderPeer != null)
|
||||
{
|
||||
this.OwningRowGroupHeaderPeer.InvalidatePeer();
|
||||
children.AddRange(this.OwningRowGroupHeaderPeer.GetChildren());
|
||||
}
|
||||
|
||||
#if FEATURE_ICOLLECTIONVIEW_GROUP
|
||||
if (_group.IsBottomLevel)
|
||||
{
|
||||
#endif
|
||||
#pragma warning disable SA1137 // Elements should have the same indentation
|
||||
foreach (object item in _group.GroupItems /*Items*/)
|
||||
{
|
||||
children.Add(this.OwningDataGridPeer.GetOrCreateItemPeer(item));
|
||||
}
|
||||
#pragma warning restore SA1137 // Elements should have the same indentation
|
||||
#if FEATURE_ICOLLECTIONVIEW_GROUP
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (object group in _group.Items)
|
||||
{
|
||||
children.Add(this.OwningDataGridPeer.GetOrCreateGroupItemPeer(group));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return children;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.GetClassName() : string.Empty;
|
||||
#if DEBUG_AUTOMATION
|
||||
Debug.WriteLine("DataGridGroupItemAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Point that represents the clickable space that is on the UIElement that is associated with this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>A Point that represents the clickable space that is on the UIElement that is associated with this DataGridGroupItemAutomationPeer.</returns>
|
||||
protected override Point GetClickablePointCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.GetClickablePoint() : new Point(double.NaN, double.NaN);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string that describes the functionality of the control that is associated with the automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the help text.</returns>
|
||||
protected override string GetHelpTextCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.GetHelpText() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that communicates the visual status of the UIElement that is associated with this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>A string that communicates the visual status of the UIElement that is associated with this DataGridGroupItemAutomationPeer.</returns>
|
||||
protected override string GetItemStatusCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.GetItemStatus() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a human-readable string that contains the item type that the UIElement for this DataGridGroupItemAutomationPeer represents.
|
||||
/// </summary>
|
||||
/// <returns>A human-readable string that contains the item type that the UIElement for this DataGridGroupItemAutomationPeer represents.</returns>
|
||||
protected override string GetItemTypeCore()
|
||||
{
|
||||
return (this.OwningRowGroupHeaderPeer != null) ? this.OwningRowGroupHeaderPeer.GetItemType() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the AutomationPeer for the element that is targeted to the UIElement for this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The AutomationPeer for the element that is targeted to the UIElement for this DataGridGroupItemAutomationPeer.</returns>
|
||||
protected override AutomationPeer GetLabeledByCore()
|
||||
{
|
||||
return (this.OwningRowGroupHeaderPeer != null) ? this.OwningRowGroupHeaderPeer.GetLabeledBy() : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a localized human readable string for this control type.
|
||||
/// </summary>
|
||||
/// <returns>A localized human readable string for this control type.</returns>
|
||||
protected override string GetLocalizedControlTypeCore()
|
||||
{
|
||||
return (this.OwningRowGroupHeaderPeer != null) ? this.OwningRowGroupHeaderPeer.GetLocalizedControlType() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string that describes the functionality of the control that is associated with this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the help text.</returns>
|
||||
protected override string GetNameCore()
|
||||
{
|
||||
#if FEATURE_ICOLLECTIONVIEW_GROUP
|
||||
if (_group.Name != null)
|
||||
{
|
||||
string name = _group.Name.ToString();
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return base.GetNameCore();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the element associated with this DataGridGroupItemAutomationPeer is laid out in a specific direction.
|
||||
/// </summary>
|
||||
/// <returns>A value indicating whether the element associated with this DataGridGroupItemAutomationPeer is laid out in a specific direction.</returns>
|
||||
protected override AutomationOrientation GetOrientationCore()
|
||||
{
|
||||
return (this.OwningRowGroupHeaderPeer != null) ? this.OwningRowGroupHeaderPeer.GetOrientation() : AutomationOrientation.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface.
|
||||
/// </summary>
|
||||
/// <param name="patternInterface">A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration.</param>
|
||||
/// <returns>The object that supports the specified pattern, or null if unsupported.</returns>
|
||||
protected override object GetPatternCore(PatternInterface patternInterface)
|
||||
{
|
||||
switch (patternInterface)
|
||||
{
|
||||
case PatternInterface.ExpandCollapse:
|
||||
case PatternInterface.Grid:
|
||||
case PatternInterface.Selection:
|
||||
case PatternInterface.Table:
|
||||
return this;
|
||||
case PatternInterface.ScrollItem:
|
||||
{
|
||||
if (this.OwningDataGrid.VerticalScrollBar != null &&
|
||||
this.OwningDataGrid.VerticalScrollBar.Maximum > 0)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetPatternCore(patternInterface);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the UIElement associated with this DataGridGroupItemAutomationPeer can accept keyboard focus.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is focusable by the keyboard; otherwise false.</returns>
|
||||
protected override bool HasKeyboardFocusCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.HasKeyboardFocus() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the element associated with this DataGridGroupItemAutomationPeer is an element that contains data that is presented to the user.
|
||||
/// </summary>
|
||||
/// <returns>True if the element contains data for the user to read; otherwise, false.</returns>
|
||||
protected override bool IsContentElementCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.IsContentElement() : true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the UIElement associated with this DataGridGroupItemAutomationPeer
|
||||
/// is understood by the end user as interactive.
|
||||
/// </summary>
|
||||
/// <returns>True if the UIElement associated with this DataGridGroupItemAutomationPeer
|
||||
/// is understood by the end user as interactive.</returns>
|
||||
protected override bool IsControlElementCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.IsControlElement() : true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this DataGridGroupItemAutomationPeer can receive and send events to the associated element.
|
||||
/// </summary>
|
||||
/// <returns>True if this DataGridGroupItemAutomationPeer can receive and send events; otherwise, false.</returns>
|
||||
protected override bool IsEnabledCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.IsEnabled() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UIElement associated with this DataGridGroupItemAutomationPeer can accept keyboard focus.
|
||||
/// </summary>
|
||||
/// <returns>True if the UIElement associated with this DataGridGroupItemAutomationPeer can accept keyboard focus.</returns>
|
||||
protected override bool IsKeyboardFocusableCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.IsKeyboardFocusable() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UIElement associated with this DataGridGroupItemAutomationPeer is off the screen.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is not on the screen; otherwise, false.</returns>
|
||||
protected override bool IsOffscreenCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.IsOffscreen() : true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UIElement associated with this DataGridGroupItemAutomationPeer contains protected content.
|
||||
/// </summary>
|
||||
/// <returns>True if the UIElement contains protected content.</returns>
|
||||
protected override bool IsPasswordCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.IsPassword() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UIElement associated with this DataGridGroupItemAutomationPeer is required to be completed on a form.
|
||||
/// </summary>
|
||||
/// <returns>True if the UIElement is required to be completed on a form.</returns>
|
||||
protected override bool IsRequiredForFormCore()
|
||||
{
|
||||
return this.OwningRowGroupHeaderPeer != null ? this.OwningRowGroupHeaderPeer.IsRequiredForForm() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the keyboard input focus on the UIElement associated with this DataGridGroupItemAutomationPeer.
|
||||
/// </summary>
|
||||
protected override void SetFocusCore()
|
||||
{
|
||||
if (this.OwningRowGroupHeaderPeer != null)
|
||||
{
|
||||
this.OwningRowGroupHeaderPeer.SetFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void IExpandCollapseProvider.Collapse()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
if (this.OwningDataGrid != null)
|
||||
{
|
||||
this.OwningDataGrid.CollapseRowGroup(_group, false /*collapseAllSubgroups*/);
|
||||
}
|
||||
}
|
||||
|
||||
void IExpandCollapseProvider.Expand()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
if (this.OwningDataGrid != null)
|
||||
{
|
||||
this.OwningDataGrid.ExpandRowGroup(_group, false /*expandAllSubgroups*/);
|
||||
}
|
||||
}
|
||||
|
||||
ExpandCollapseState IExpandCollapseProvider.ExpandCollapseState
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningDataGrid != null)
|
||||
{
|
||||
DataGridRowGroupInfo groupInfo = this.OwningDataGrid.RowGroupInfoFromCollectionViewGroup(_group);
|
||||
if (groupInfo != null && groupInfo.Visibility == Visibility.Visible)
|
||||
{
|
||||
return ExpandCollapseState.Expanded;
|
||||
}
|
||||
}
|
||||
|
||||
return ExpandCollapseState.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
int IGridProvider.ColumnCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningDataGrid != null)
|
||||
{
|
||||
return this.OwningDataGrid.Columns.Count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple IGridProvider.GetItem(int row, int column)
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
if (this.OwningDataGrid != null &&
|
||||
this.OwningDataGrid.DataConnection != null &&
|
||||
row >= 0 && row < _group.GroupItems.Count /*ItemCount*/ &&
|
||||
column >= 0 && column < this.OwningDataGrid.Columns.Count)
|
||||
{
|
||||
DataGridRowGroupInfo groupInfo = this.OwningDataGrid.RowGroupInfoFromCollectionViewGroup(_group);
|
||||
if (groupInfo != null)
|
||||
{
|
||||
// Adjust the row index to be relative to the DataGrid instead of the group
|
||||
row = groupInfo.Slot - this.OwningDataGrid.RowGroupHeadersTable.GetIndexCount(0, groupInfo.Slot) + row + 1;
|
||||
Debug.Assert(row >= 0, "Expected positive row.");
|
||||
Debug.Assert(row < this.OwningDataGrid.DataConnection.Count, "Expected row smaller than this.OwningDataGrid.DataConnection.Count.");
|
||||
int slot = this.OwningDataGrid.SlotFromRowIndex(row);
|
||||
|
||||
if (!this.OwningDataGrid.IsSlotVisible(slot))
|
||||
{
|
||||
object item = this.OwningDataGrid.DataConnection.GetDataItem(row);
|
||||
this.OwningDataGrid.ScrollIntoView(item, this.OwningDataGrid.Columns[column]);
|
||||
}
|
||||
|
||||
Debug.Assert(this.OwningDataGrid.IsSlotVisible(slot), "Expected OwningDataGrid.IsSlotVisible(slot) is true.");
|
||||
|
||||
DataGridRow dgr = this.OwningDataGrid.DisplayData.GetDisplayedElement(slot) as DataGridRow;
|
||||
|
||||
// the first cell is always the indentation filler cell if grouping is enabled, so skip it
|
||||
Debug.Assert(column + 1 < dgr.Cells.Count, "Expected column + 1 smaller than dgr.Cells.Count.");
|
||||
DataGridCell cell = dgr.Cells[column + 1];
|
||||
AutomationPeer peer = CreatePeerForElement(cell);
|
||||
if (peer != null)
|
||||
{
|
||||
return ProviderFromPeer(peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
int IGridProvider.RowCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _group.GroupItems.Count /*ItemCount*/;
|
||||
}
|
||||
}
|
||||
|
||||
void IScrollItemProvider.ScrollIntoView()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
if (this.OwningDataGrid != null)
|
||||
{
|
||||
DataGridRowGroupInfo groupInfo = this.OwningDataGrid.RowGroupInfoFromCollectionViewGroup(_group);
|
||||
if (groupInfo != null)
|
||||
{
|
||||
this.OwningDataGrid.ScrollIntoView(groupInfo.CollectionViewGroup, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple[] ISelectionProvider.GetSelection()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
if (this.OwningDataGrid != null &&
|
||||
this.OwningDataGridPeer != null &&
|
||||
this.OwningDataGrid.SelectedItems != null &&
|
||||
_group.GroupItems.Count /*ItemCount*/ > 0)
|
||||
{
|
||||
DataGridRowGroupInfo groupInfo = this.OwningDataGrid.RowGroupInfoFromCollectionViewGroup(_group);
|
||||
if (groupInfo != null)
|
||||
{
|
||||
// See which of the selected items are contained within this group
|
||||
List<IRawElementProviderSimple> selectedProviders = new List<IRawElementProviderSimple>();
|
||||
int startRowIndex = groupInfo.Slot - this.OwningDataGrid.RowGroupHeadersTable.GetIndexCount(0, groupInfo.Slot) + 1;
|
||||
foreach (object item in this.OwningDataGrid.GetSelectionInclusive(startRowIndex, startRowIndex + _group.GroupItems.Count /*ItemCount*/ - 1))
|
||||
{
|
||||
DataGridItemAutomationPeer peer = this.OwningDataGridPeer.GetOrCreateItemPeer(item);
|
||||
if (peer != null)
|
||||
{
|
||||
selectedProviders.Add(ProviderFromPeer(peer));
|
||||
}
|
||||
}
|
||||
|
||||
return selectedProviders.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool ISelectionProvider.CanSelectMultiple
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningDataGrid != null && this.OwningDataGrid.SelectionMode == DataGridSelectionMode.Extended;
|
||||
}
|
||||
}
|
||||
|
||||
bool ISelectionProvider.IsSelectionRequired
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureEnabled()
|
||||
{
|
||||
if (!_dataGridAutomationPeer.IsEnabled())
|
||||
{
|
||||
throw new ElementNotEnabledException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,541 @@
|
|||
// 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 Microsoft.Toolkit.Uwp.UI.Controls;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Automation;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for an item in a DataGrid
|
||||
/// </summary>
|
||||
public class DataGridItemAutomationPeer : FrameworkElementAutomationPeer,
|
||||
IInvokeProvider, IScrollItemProvider, ISelectionItemProvider, ISelectionProvider
|
||||
{
|
||||
private object _item;
|
||||
private AutomationPeer _dataGridAutomationPeer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridItemAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
public DataGridItemAutomationPeer(object item, DataGrid dataGrid)
|
||||
: base(dataGrid)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException("item");
|
||||
}
|
||||
|
||||
if (dataGrid == null)
|
||||
{
|
||||
throw new ArgumentNullException("dataGrid");
|
||||
}
|
||||
|
||||
_item = item;
|
||||
_dataGridAutomationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(dataGrid);
|
||||
}
|
||||
|
||||
private DataGrid OwningDataGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
DataGridAutomationPeer gridPeer = _dataGridAutomationPeer as DataGridAutomationPeer;
|
||||
return gridPeer.Owner as DataGrid;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridRow OwningRow
|
||||
{
|
||||
get
|
||||
{
|
||||
int index = this.OwningDataGrid.DataConnection.IndexOf(_item);
|
||||
int slot = this.OwningDataGrid.SlotFromRowIndex(index);
|
||||
|
||||
if (this.OwningDataGrid.IsSlotVisible(slot))
|
||||
{
|
||||
return this.OwningDataGrid.DisplayData.GetDisplayedElement(slot) as DataGridRow;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridRowAutomationPeer OwningRowPeer
|
||||
{
|
||||
get
|
||||
{
|
||||
DataGridRowAutomationPeer rowPeer = null;
|
||||
DataGridRow row = this.OwningRow;
|
||||
if (row != null)
|
||||
{
|
||||
rowPeer = FrameworkElementAutomationPeer.CreatePeerForElement(row) as DataGridRowAutomationPeer;
|
||||
}
|
||||
|
||||
return rowPeer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the accelerator key for the UIElement that is associated with this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The accelerator key for the UIElement that is associated with this DataGridItemAutomationPeer.</returns>
|
||||
protected override string GetAcceleratorKeyCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetAcceleratorKey() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the access key for the UIElement that is associated with this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The access key for the UIElement that is associated with this DataGridItemAutomationPeer.</returns>
|
||||
protected override string GetAccessKeyCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetAccessKey() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the control type for the UIElement that is associated with this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The control type for the UIElement that is associated with this DataGridItemAutomationPeer.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.DataItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string that uniquely identifies the FrameworkElement that is associated with this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that uniquely identifies the FrameworkElement that is associated with this DataGridItemAutomationPeer.</returns>
|
||||
protected override string GetAutomationIdCore()
|
||||
{
|
||||
// The AutomationId should be unset for dynamic content.
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Rect that represents the bounding rectangle of the UIElement that is associated with this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The Rect that represents the bounding rectangle of the UIElement that is associated with this DataGridItemAutomationPeer.</returns>
|
||||
protected override Rect GetBoundingRectangleCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetBoundingRectangle() : default(Rect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the collection of elements that are represented in the UI Automation tree as immediate
|
||||
/// child elements of the automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The children elements.</returns>
|
||||
protected override IList<AutomationPeer> GetChildrenCore()
|
||||
{
|
||||
if (this.OwningRowPeer != null)
|
||||
{
|
||||
this.OwningRowPeer.InvalidatePeer();
|
||||
return this.OwningRowPeer.GetChildren();
|
||||
}
|
||||
|
||||
return new List<AutomationPeer>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = (this.OwningRowPeer != null) ? this.OwningRowPeer.GetClassName() : string.Empty;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridItemAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Point that represents the clickable space that is on the UIElement that is associated with this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>A Point that represents the clickable space that is on the UIElement that is associated with this DataGridItemAutomationPeer.</returns>
|
||||
protected override Point GetClickablePointCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetClickablePoint() : new Point(double.NaN, double.NaN);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string that describes the functionality of the control that is associated with the automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the help text.</returns>
|
||||
protected override string GetHelpTextCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetHelpText() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that communicates the visual status of the UIElement that is associated with this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>A string that communicates the visual status of the UIElement that is associated with this DataGridItemAutomationPeer.</returns>
|
||||
protected override string GetItemStatusCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetItemStatus() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a human-readable string that contains the item type that the UIElement for this DataGridItemAutomationPeer represents.
|
||||
/// </summary>
|
||||
/// <returns>A human-readable string that contains the item type that the UIElement for this DataGridItemAutomationPeer represents.</returns>
|
||||
protected override string GetItemTypeCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetItemType() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the AutomationPeer for the element that is targeted to the UIElement for this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The AutomationPeer for the element that is targeted to the UIElement for this DataGridItemAutomationPeer.</returns>
|
||||
protected override AutomationPeer GetLabeledByCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetLabeledBy() : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a localized human readable string for this control type.
|
||||
/// </summary>
|
||||
/// <returns>A localized human readable string for this control type.</returns>
|
||||
protected override string GetLocalizedControlTypeCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetLocalizedControlType() : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string that describes the functionality of the control that is associated with this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the help text.</returns>
|
||||
protected override string GetNameCore()
|
||||
{
|
||||
if (this.OwningRowPeer != null)
|
||||
{
|
||||
string owningRowPeerName = this.OwningRowPeer.GetName();
|
||||
if (!string.IsNullOrEmpty(owningRowPeerName))
|
||||
{
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridItemAutomationPeer.GetNameCore returns " + owningRowPeerName);
|
||||
#endif
|
||||
return owningRowPeerName;
|
||||
}
|
||||
}
|
||||
|
||||
string name = UI.Controls.Properties.Resources.DataGridRowAutomationPeer_ItemType;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridItemAutomationPeer.GetNameCore returns " + name);
|
||||
#endif
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the element associated with this DataGridItemAutomationPeer is laid out in a specific direction.
|
||||
/// </summary>
|
||||
/// <returns>A value indicating whether the element associated with this DataGridItemAutomationPeer is laid out in a specific direction.</returns>
|
||||
protected override AutomationOrientation GetOrientationCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.GetOrientation() : AutomationOrientation.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the control pattern that is associated with the specified Windows.UI.Xaml.Automation.Peers.PatternInterface.
|
||||
/// </summary>
|
||||
/// <param name="patternInterface">A value from the Windows.UI.Xaml.Automation.Peers.PatternInterface enumeration.</param>
|
||||
/// <returns>The object that supports the specified pattern, or null if unsupported.</returns>
|
||||
protected override object GetPatternCore(PatternInterface patternInterface)
|
||||
{
|
||||
switch (patternInterface)
|
||||
{
|
||||
case PatternInterface.Invoke:
|
||||
{
|
||||
if (!this.OwningDataGrid.IsReadOnly)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PatternInterface.ScrollItem:
|
||||
{
|
||||
if (this.OwningDataGrid.VerticalScrollBar != null &&
|
||||
this.OwningDataGrid.VerticalScrollBar.Maximum > 0)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PatternInterface.Selection:
|
||||
case PatternInterface.SelectionItem:
|
||||
return this;
|
||||
}
|
||||
|
||||
return base.GetPatternCore(patternInterface);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the UIElement associated with this DataGridItemAutomationPeer can accept keyboard focus.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is focusable by the keyboard; otherwise false.</returns>
|
||||
protected override bool HasKeyboardFocusCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.HasKeyboardFocus() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether the element associated with this DataGridItemAutomationPeer is an element that contains data that is presented to the user.
|
||||
/// </summary>
|
||||
/// <returns>True if the element contains data for the user to read; otherwise, false.</returns>
|
||||
protected override bool IsContentElementCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.IsContentElement() : true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the UIElement associated with this DataGridItemAutomationPeer
|
||||
/// is understood by the end user as interactive.
|
||||
/// </summary>
|
||||
/// <returns>True if the UIElement associated with this DataGridItemAutomationPeer
|
||||
/// is understood by the end user as interactive.</returns>
|
||||
protected override bool IsControlElementCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.IsControlElement() : true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this DataGridItemAutomationPeer can receive and send events to the associated element.
|
||||
/// </summary>
|
||||
/// <returns>True if this DataGridItemAutomationPeer can receive and send events; otherwise, false.</returns>
|
||||
protected override bool IsEnabledCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.IsEnabled() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UIElement associated with this DataGridItemAutomationPeer can accept keyboard focus.
|
||||
/// </summary>
|
||||
/// <returns>True if the UIElement associated with this DataGridItemAutomationPeer can accept keyboard focus.</returns>
|
||||
protected override bool IsKeyboardFocusableCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.IsKeyboardFocusable() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UIElement associated with this DataGridItemAutomationPeer is off the screen.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is not on the screen; otherwise, false.</returns>
|
||||
protected override bool IsOffscreenCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.IsOffscreen() : true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UIElement associated with this DataGridItemAutomationPeer contains protected content.
|
||||
/// </summary>
|
||||
/// <returns>True if the UIElement contains protected content.</returns>
|
||||
protected override bool IsPasswordCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.IsPassword() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the UIElement associated with this DataGridItemAutomationPeer is required to be completed on a form.
|
||||
/// </summary>
|
||||
/// <returns>True if the UIElement is required to be completed on a form.</returns>
|
||||
protected override bool IsRequiredForFormCore()
|
||||
{
|
||||
return this.OwningRowPeer != null ? this.OwningRowPeer.IsRequiredForForm() : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the keyboard input focus on the UIElement associated with this DataGridItemAutomationPeer.
|
||||
/// </summary>
|
||||
protected override void SetFocusCore()
|
||||
{
|
||||
if (this.OwningRowPeer != null)
|
||||
{
|
||||
this.OwningRowPeer.SetFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void IInvokeProvider.Invoke()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
if (this.OwningRowPeer == null)
|
||||
{
|
||||
this.OwningDataGrid.ScrollIntoView(_item, null);
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (this.OwningRow != null)
|
||||
{
|
||||
if (this.OwningDataGrid.WaitForLostFocus(() => { ((IInvokeProvider)this).Invoke(); }))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.OwningDataGrid.EditingRow == this.OwningRow)
|
||||
{
|
||||
success = this.OwningDataGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/);
|
||||
}
|
||||
else if (this.OwningDataGrid.UpdateSelectionAndCurrency(this.OwningDataGrid.CurrentColumnIndex, this.OwningRow.Slot, DataGridSelectionAction.SelectCurrent, false))
|
||||
{
|
||||
success = this.OwningDataGrid.BeginEdit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IScrollItemProvider.ScrollIntoView()
|
||||
{
|
||||
this.OwningDataGrid.ScrollIntoView(_item, null);
|
||||
}
|
||||
|
||||
bool ISelectionItemProvider.IsSelected
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.OwningDataGrid.SelectedItems.Contains(_item);
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple ISelectionItemProvider.SelectionContainer
|
||||
{
|
||||
get
|
||||
{
|
||||
return ProviderFromPeer(_dataGridAutomationPeer);
|
||||
}
|
||||
}
|
||||
|
||||
void ISelectionItemProvider.AddToSelection()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
if (this.OwningDataGrid.SelectionMode == DataGridSelectionMode.Single &&
|
||||
this.OwningDataGrid.SelectedItems.Count > 0 &&
|
||||
!this.OwningDataGrid.SelectedItems.Contains(_item))
|
||||
{
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
|
||||
int index = this.OwningDataGrid.DataConnection.IndexOf(_item);
|
||||
if (index != -1)
|
||||
{
|
||||
this.OwningDataGrid.SetRowSelection(this.OwningDataGrid.SlotFromRowIndex(index), true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
|
||||
void ISelectionItemProvider.RemoveFromSelection()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
int index = this.OwningDataGrid.DataConnection.IndexOf(_item);
|
||||
if (index != -1)
|
||||
{
|
||||
bool success = true;
|
||||
if (this.OwningDataGrid.EditingRow != null && this.OwningDataGrid.EditingRow.Index == index)
|
||||
{
|
||||
if (this.OwningDataGrid.WaitForLostFocus(() => { ((ISelectionItemProvider)this).RemoveFromSelection(); }))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
success = this.OwningDataGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/);
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
this.OwningDataGrid.SetRowSelection(this.OwningDataGrid.SlotFromRowIndex(index), false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
}
|
||||
|
||||
void ISelectionItemProvider.Select()
|
||||
{
|
||||
EnsureEnabled();
|
||||
|
||||
int index = this.OwningDataGrid.DataConnection.IndexOf(_item);
|
||||
if (index != -1)
|
||||
{
|
||||
bool success = true;
|
||||
if (this.OwningDataGrid.EditingRow != null && this.OwningDataGrid.EditingRow.Index != index)
|
||||
{
|
||||
if (this.OwningDataGrid.WaitForLostFocus(() => { ((ISelectionItemProvider)this).Select(); }))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
success = this.OwningDataGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/);
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Clear all the other selected items and select this one
|
||||
int slot = this.OwningDataGrid.SlotFromRowIndex(index);
|
||||
this.OwningDataGrid.UpdateSelectionAndCurrency(this.OwningDataGrid.CurrentColumnIndex, slot, DataGridSelectionAction.SelectCurrent, false);
|
||||
return;
|
||||
}
|
||||
|
||||
throw DataGridError.DataGridAutomationPeer.OperationCannotBePerformed();
|
||||
}
|
||||
}
|
||||
|
||||
bool ISelectionProvider.CanSelectMultiple
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ISelectionProvider.IsSelectionRequired
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IRawElementProviderSimple[] ISelectionProvider.GetSelection()
|
||||
{
|
||||
if (this.OwningRow != null &&
|
||||
this.OwningDataGrid.IsSlotVisible(this.OwningRow.Slot) &&
|
||||
this.OwningDataGrid.CurrentSlot == this.OwningRow.Slot)
|
||||
{
|
||||
DataGridCell cell = this.OwningRow.Cells[this.OwningRow.OwningGrid.CurrentColumnIndex];
|
||||
AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(cell);
|
||||
if (peer != null)
|
||||
{
|
||||
return new IRawElementProviderSimple[] { ProviderFromPeer(peer) };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void EnsureEnabled()
|
||||
{
|
||||
if (!_dataGridAutomationPeer.IsEnabled())
|
||||
{
|
||||
throw new ElementNotEnabledException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// 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;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for DataGridRow
|
||||
/// </summary>
|
||||
public class DataGridRowAutomationPeer : FrameworkElementAutomationPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">DataGridRow</param>
|
||||
public DataGridRowAutomationPeer(DataGridRow owner)
|
||||
: base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control type for the element that is associated with the UI Automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The control type.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.DataItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = Owner.GetType().Name;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridRowAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a human-readable string that contains the item type that the UIElement for this DataGridRowAutomationPeer represents.
|
||||
/// </summary>
|
||||
/// <returns>A human-readable string that contains the item type that the UIElement for this DataGridRowAutomationPeer represents.</returns>
|
||||
protected override string GetItemTypeCore()
|
||||
{
|
||||
string itemType = base.GetItemTypeCore();
|
||||
if (!string.IsNullOrEmpty(itemType))
|
||||
{
|
||||
return itemType;
|
||||
}
|
||||
|
||||
return UI.Controls.Properties.Resources.DataGridRowAutomationPeer_ItemType;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// 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;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for DataGridRowGroupHeader
|
||||
/// </summary>
|
||||
public class DataGridRowGroupHeaderAutomationPeer : FrameworkElementAutomationPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowGroupHeaderAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">DataGridRowGroupHeader</param>
|
||||
public DataGridRowGroupHeaderAutomationPeer(DataGridRowGroupHeader owner)
|
||||
: base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control type for the element that is associated with the UI Automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The control type.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.Group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = Owner.GetType().Name;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridRowGroupHeaderAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// 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.Primitives;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for DataGridRowHeader
|
||||
/// </summary>
|
||||
public class DataGridRowHeaderAutomationPeer : FrameworkElementAutomationPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowHeaderAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">DataGridRowHeader</param>
|
||||
public DataGridRowHeaderAutomationPeer(DataGridRowHeader owner)
|
||||
: base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
private DataGridRowHeader OwningHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return (DataGridRowHeader)Owner;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control type for the element that is associated with the UI Automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The control type.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.HeaderItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = Owner.GetType().Name;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridRowHeaderAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the element.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetNameCore()
|
||||
{
|
||||
return (this.OwningHeader.Content as string) ?? base.GetNameCore();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that specifies whether the element is a content element.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is a content element; otherwise false</returns>
|
||||
protected override bool IsContentElementCore()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// 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 Microsoft.Toolkit.Uwp.UI.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Automation.Peers
|
||||
{
|
||||
/// <summary>
|
||||
/// AutomationPeer for the <see cref="DataGridRowsPresenter"/> class.
|
||||
/// </summary>
|
||||
public class DataGridRowsPresenterAutomationPeer : FrameworkElementAutomationPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowsPresenterAutomationPeer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">Owning DataGridRowsPresenter</param>
|
||||
public DataGridRowsPresenterAutomationPeer(DataGridRowsPresenter owner)
|
||||
: base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
private DataGridAutomationPeer GridPeer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningRowsPresenter.OwningGrid != null)
|
||||
{
|
||||
return CreatePeerForElement(this.OwningRowsPresenter.OwningGrid) as DataGridAutomationPeer;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridRowsPresenter OwningRowsPresenter
|
||||
{
|
||||
get
|
||||
{
|
||||
return Owner as DataGridRowsPresenter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control type for the element that is associated with the UI Automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The control type.</returns>
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.Custom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of elements that are represented in the UI Automation tree as immediate
|
||||
/// child elements of the automation peer.
|
||||
/// </summary>
|
||||
/// <returns>The children elements.</returns>
|
||||
protected override IList<AutomationPeer> GetChildrenCore()
|
||||
{
|
||||
if (this.OwningRowsPresenter.OwningGrid == null)
|
||||
{
|
||||
return new List<AutomationPeer>();
|
||||
}
|
||||
|
||||
return this.GridPeer.GetChildPeers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GetClassName that gets a human readable name that, in addition to AutomationControlType,
|
||||
/// differentiates the control represented by this AutomationPeer.
|
||||
/// </summary>
|
||||
/// <returns>The string that contains the name.</returns>
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
string classNameCore = Owner.GetType().Name;
|
||||
#if DEBUG_AUTOMATION
|
||||
System.Diagnostics.Debug.WriteLine("DataGridRowsPresenterAutomationPeer.GetClassNameCore returns " + classNameCore);
|
||||
#endif
|
||||
return classNameCore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that specifies whether the element is a content element.
|
||||
/// </summary>
|
||||
/// <returns>True if the element is a content element; otherwise false</returns>
|
||||
protected override bool IsContentElementCore()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,969 @@
|
|||
<!--
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
-->
|
||||
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
xmlns:localprimitives="using:Microsoft.Toolkit.Uwp.UI.Controls.Primitives">
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<SolidColorBrush x:Key="InvalidBrush" Color="#FFFF00"/>
|
||||
<SolidColorBrush x:Key="FillerGridLinesBrush" Color="Transparent"/>
|
||||
<StaticResource x:Key="ScrollBarsSeparatorBackground" ResourceKey="SystemControlPageBackgroundChromeLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderBackgroundColor" ResourceKey="SystemAltHighColor"/>
|
||||
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{StaticResource DataGridColumnHeaderBackgroundColor}"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderHoveredBackgroundColor" ResourceKey="SystemListLowColor"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPressedBackgroundColor" ResourceKey="SystemListMediumColor"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderDraggedBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPointerOverBrush" ResourceKey="SystemControlHighlightListLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPressedBrush" ResourceKey="SystemControlHighlightListMediumBrush"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<SolidColorBrush x:Key="InvalidBrush" Color="#FFFF00"/>
|
||||
<SolidColorBrush x:Key="FillerGridLinesBrush" Color="Transparent"/>
|
||||
<StaticResource x:Key="ScrollBarsSeparatorBackground" ResourceKey="SystemControlPageBackgroundChromeLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderBackgroundColor" ResourceKey="SystemAltHighColor"/>
|
||||
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{StaticResource DataGridColumnHeaderBackgroundColor}"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderHoveredBackgroundColor" ResourceKey="SystemListLowColor"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPressedBackgroundColor" ResourceKey="SystemListMediumColor"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderDraggedBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPointerOverBrush" ResourceKey="SystemControlHighlightListLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPressedBrush" ResourceKey="SystemControlHighlightListMediumBrush"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="InvalidBrush" Color="#C50500"/>
|
||||
<SolidColorBrush x:Key="FillerGridLinesBrush" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ScrollBarsSeparatorBackground" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderBackgroundColor" ResourceKey="SystemAltHighColor"/>
|
||||
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{StaticResource DataGridColumnHeaderBackgroundColor}"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderHoveredBackgroundColor" ResourceKey="SystemListLowColor"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPressedBackgroundColor" ResourceKey="SystemListMediumColor"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderDraggedBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPointerOverBrush" ResourceKey="SystemControlHighlightListLowBrush"/>
|
||||
<StaticResource x:Key="DataGridColumnHeaderPressedBrush" ResourceKey="SystemControlHighlightListMediumBrush"/>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<SolidColorBrush x:Key="SystemControlGridLinesBaseMediumLowBrush" Color="{StaticResource SystemBaseMediumLowColor}" Opacity="0.4"/>
|
||||
<SolidColorBrush x:Key="SystemControlRowGroupHeaderBackgroundMediumBrush" Color="{StaticResource SystemChromeMediumColor}"/>
|
||||
<SolidColorBrush x:Key="DataGridCurrencyVisualPrimaryBrush" Color="Transparent"/>
|
||||
|
||||
<x:String x:Key="SortIconAscending"></x:String>
|
||||
<x:String x:Key="SortIconDescending"></x:String>
|
||||
<x:String x:Key="RowGroupHeaderIconClosed"></x:String>
|
||||
<x:String x:Key="RowGroupHeaderIconOpened"></x:String>
|
||||
|
||||
<x:String x:Key="ScrollBarsSeparatorExpandBeginTime">00:00:00.40</x:String>
|
||||
<x:String x:Key="ScrollBarsSeparatorExpandDuration">00:00:00.1</x:String>
|
||||
<x:String x:Key="ScrollBarsSeparatorContractBeginTime">00:00:02.00</x:String>
|
||||
<x:String x:Key="ScrollBarsSeparatorContractDelay">00:00:02</x:String>
|
||||
<x:String x:Key="ScrollBarsSeparatorContractDuration">00:00:00.1</x:String>
|
||||
<x:String x:Key="ScrollBarsSeparatorContractFinalKeyframe">00:00:02.1</x:String>
|
||||
<x:String x:Key="ScrollBarsSeparatorNoTouchDuration">00:00:00.5</x:String>
|
||||
|
||||
<x:Double x:Key="ListAccentLowOpacity">0.6</x:Double>
|
||||
<x:Double x:Key="ListAccentMediumOpacity">0.8</x:Double>
|
||||
|
||||
<StaticResource x:Key="GridLinesBrush" ResourceKey="SystemControlGridLinesBaseMediumLowBrush"/>
|
||||
|
||||
<StaticResource x:Key="DataGridDetailsPresenterBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumLowBrush"/>
|
||||
<StaticResource x:Key="DataGridFillerColumnGridLinesBrush" ResourceKey="FillerGridLinesBrush"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedBackgroundColor" ResourceKey="SystemAccentColor"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedBackgroundOpacity" ResourceKey="ListAccentLowOpacity"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedHoveredBackgroundColor" ResourceKey="SystemAccentColor"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedHoveredBackgroundOpacity" ResourceKey="ListAccentMediumOpacity"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundColor" ResourceKey="SystemAccentColor"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundOpacity" ResourceKey="ListAccentLowOpacity"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundColor" ResourceKey="SystemAccentColor"/>
|
||||
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundOpacity" ResourceKey="ListAccentMediumOpacity"/>
|
||||
<StaticResource x:Key="DataGridRowHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowHeaderBackgroundBrush" ResourceKey="SystemControlBackgroundAltHighBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderBackgroundBrush" ResourceKey="SystemControlBackgroundChromeMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" ResourceKey="SystemControlBackgroundListLowBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" ResourceKey="SystemControlBackgroundListMediumBrush"/>
|
||||
<StaticResource x:Key="DataGridRowGroupHeaderForegroundBrush" ResourceKey="SystemControlForegroundBaseHighBrush"/>
|
||||
<StaticResource x:Key="DataGridRowInvalidBrush" ResourceKey="InvalidBrush"/>
|
||||
<StaticResource x:Key="DataGridCellBackgroundBrush" ResourceKey="SystemControlTransparentBrush"/>
|
||||
<StaticResource x:Key="DataGridCellFocusVisualPrimaryBrush" ResourceKey="SystemControlFocusVisualPrimaryBrush"/>
|
||||
<StaticResource x:Key="DataGridCellFocusVisualSecondaryBrush" ResourceKey="SystemControlFocusVisualSecondaryBrush"/>
|
||||
<StaticResource x:Key="DataGridCellInvalidBrush" ResourceKey="InvalidBrush"/>
|
||||
|
||||
<Style TargetType="local:DataGrid">
|
||||
<Setter Property="DragIndicatorStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="ContentControl">
|
||||
<Setter Property="Foreground" Value="{ThemeResource DataGridColumnHeaderForegroundBrush}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ContentControl">
|
||||
<Grid Background="{ThemeResource DataGridColumnHeaderDraggedBackgroundBrush}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="SortStates">
|
||||
<VisualState x:Name="Unsorted"/>
|
||||
<VisualState x:Name="SortAscending">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SortDescending">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="SortIcon.Glyph" Value="{ThemeResource SortIconDescending}"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Margin="{TemplateBinding Padding}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition MinWidth="32" Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ContentPresenter Content="{TemplateBinding Content}"/>
|
||||
|
||||
<FontIcon x:Name="SortIcon" Grid.Column="1" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="{ThemeResource SortIconAscending}"
|
||||
FontSize="12" Foreground="{ThemeResource DataGridColumnHeaderForegroundBrush}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" Opacity="0"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="DropLocationIndicatorStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="Control">
|
||||
<Setter Property="Background" Value="#FF3F4346"/>
|
||||
<Setter Property="Width" Value="2"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Control">
|
||||
<Rectangle Fill="{TemplateBinding Background}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="HorizontalGridLinesBrush" Value="{ThemeResource GridLinesBrush}"/>
|
||||
<Setter Property="UseSystemFocusVisuals" Value="False"/>
|
||||
<Setter Property="IsTabStop" Value="True"/>
|
||||
<Setter Property="VerticalGridLinesBrush" Value="{ThemeResource GridLinesBrush}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:DataGrid">
|
||||
<Grid x:Name="Root" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="ScrollBarsStates">
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition From="MouseIndicator" To="NoIndicator">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="{ThemeResource ScrollBarsSeparatorContractDelay}">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>None</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="{ThemeResource ScrollBarsSeparatorContractDelay}">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>None</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition From="MouseIndicatorFull" To="NoIndicator">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="{ThemeResource ScrollBarsSeparatorContractDelay}">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>None</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="{ThemeResource ScrollBarsSeparatorContractDelay}">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>None</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition From="MouseIndicatorFull" To="MouseIndicator">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="{ThemeResource ScrollBarsSeparatorContractDelay}">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="{ThemeResource ScrollBarsSeparatorContractDelay}">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
<VisualTransition From="TouchIndicator" To="NoIndicator">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="{ThemeResource ScrollBarsSeparatorNoTouchDuration}">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>None</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="{ThemeResource ScrollBarsSeparatorNoTouchDuration}">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>None</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
</VisualStateGroup.Transitions>
|
||||
|
||||
<VisualState x:Name="NoIndicator"/>
|
||||
|
||||
<VisualState x:Name="TouchIndicator">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>TouchIndicator</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>TouchIndicator</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="MouseIndicator">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="MouseIndicatorFull">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="IndicatorMode">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<ScrollingIndicatorMode>MouseIndicator</ScrollingIndicatorMode>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="ScrollBarsSeparatorStates">
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition From="SeparatorExpanded" To="SeparatorCollapsed">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Duration="{ThemeResource ScrollBarsSeparatorContractDuration}"
|
||||
BeginTime="{ThemeResource ScrollBarsSeparatorContractBeginTime}"
|
||||
Storyboard.TargetName="BottomRightCorner"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"/>
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
</VisualStateGroup.Transitions>
|
||||
<VisualState x:Name="SeparatorCollapsed"/>
|
||||
<VisualState x:Name="SeparatorExpanded">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Duration="{ThemeResource ScrollBarsSeparatorExpandDuration}"
|
||||
BeginTime="{ThemeResource ScrollBarsSeparatorExpandBeginTime}"
|
||||
Storyboard.TargetName="BottomRightCorner"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SeparatorExpandedWithoutAnimation">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Duration="0"
|
||||
BeginTime="{ThemeResource ScrollBarsSeparatorExpandBeginTime}"
|
||||
Storyboard.TargetName="BottomRightCorner"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SeparatorCollapsedWithoutAnimation" >
|
||||
<Storyboard>
|
||||
<DoubleAnimation Duration="0"
|
||||
BeginTime="{ThemeResource ScrollBarsSeparatorContractBeginTime}"
|
||||
Storyboard.TargetName="BottomRightCorner"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid Background="{TemplateBinding Background}">
|
||||
<Grid.Resources>
|
||||
<!--Start: TopLeftHeaderTemplate-->
|
||||
<ControlTemplate x:Key="TopLeftHeaderTemplate" TargetType="localprimitives:DataGridColumnHeader">
|
||||
<Grid x:Name="TopLeftHeaderRoot">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border BorderThickness="0,0,1,0" BorderBrush="{ThemeResource GridLinesBrush}" Grid.RowSpan="2"/>
|
||||
<Rectangle VerticalAlignment="Bottom" Width="Auto" StrokeThickness="1" Height="1" Fill="{ThemeResource GridLinesBrush}" Grid.RowSpan="2"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
<!--End: TopLeftHeaderTemplate-->
|
||||
|
||||
<!--Start: TopRightHeaderTemplate-->
|
||||
<ControlTemplate x:Key="TopRightHeaderTemplate" TargetType="localprimitives:DataGridColumnHeader">
|
||||
<Grid x:Name="RootElement"/>
|
||||
</ControlTemplate>
|
||||
<!--End: TopRightHeaderTemplate-->
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- For ValidationSummary addition
|
||||
<RowDefinition Height="Auto"/>
|
||||
-->
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<localprimitives:DataGridColumnHeader x:Name="TopLeftCornerHeader" Template="{StaticResource TopLeftHeaderTemplate}"/>
|
||||
<localprimitives:DataGridColumnHeadersPresenter x:Name="ColumnHeadersPresenter" Grid.Column="1" Grid.ColumnSpan="2"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
<!-- Use TopRightCornerHeader when ColumnHeadersPresenter's Grid.ColumnSpan is 1 and the vertical ScrollBar does not paint on top of the cells -->
|
||||
<!--<localprimitives:DataGridColumnHeader x:Name="TopRightCornerHeader" Grid.Column="2" Template="{StaticResource TopRightHeaderTemplate}"/>-->
|
||||
<Rectangle x:Name="ColumnHeadersAndRowsSeparator" Grid.ColumnSpan="3" VerticalAlignment="Bottom" Width="Auto" StrokeThickness="1" Height="1" Fill="{ThemeResource GridLinesBrush}"/>
|
||||
|
||||
<localprimitives:DataGridRowsPresenter x:Name="RowsPresenter" Grid.ColumnSpan="3" Grid.Row="1" Grid.RowSpan="2"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
<Rectangle x:Name="BottomRightCorner" Fill="{ThemeResource ScrollBarsSeparatorBackground}" Opacity="0" Grid.Row="2" Grid.Column="2"/>
|
||||
<!-- Use BottomLeftCorner when RowsPresenter's Grid.RowSpan is 1 and the horizontal ScrollBar does not paint on top of the cells -->
|
||||
<!--<Rectangle x:Name="BottomLeftCorner" Fill="#FFE9EEF4" Grid.Row="2" Grid.ColumnSpan="2"/>-->
|
||||
<ScrollBar x:Name="VerticalScrollBar" Orientation="Vertical" Visibility="Collapsed" Grid.Column="2" Grid.Row="1"/>
|
||||
|
||||
<Grid Grid.Column="1" Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle x:Name="FrozenColumnScrollBarSpacer"/>
|
||||
<ScrollBar x:Name="HorizontalScrollBar" Grid.Column="1" Orientation="Horizontal" Visibility="Collapsed"/>
|
||||
</Grid>
|
||||
|
||||
<!--<dataInput:ValidationSummary x:Name="ValidationSummary" Grid.Row="3" Grid.ColumnSpan="3" MaxHeight="90"/>-->
|
||||
</Grid>
|
||||
<Border x:Name="DisabledVisualElement" IsHitTestVisible="False" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" CornerRadius="2" Background="#8CFFFFFF" Opacity="0"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="local:DataGridCell">
|
||||
<Setter Property="Background" Value="{ThemeResource DataGridCellBackgroundBrush}"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="MinHeight" Value="32"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:DataGridCell">
|
||||
<Grid x:Name="CellRoot" Background="{TemplateBinding Background}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CurrentStates">
|
||||
<VisualState x:Name="Regular"/>
|
||||
<VisualState x:Name="Current">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="CurrencyVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
|
||||
<DoubleAnimation Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="CurrentWithFocus">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="CurrencyVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
|
||||
<DoubleAnimation Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ValidationStates">
|
||||
<VisualState x:Name="Valid"/>
|
||||
<VisualState x:Name="Invalid">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="InvalidVisualElement" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Rectangle x:Name="CurrencyVisual" Stroke="{ThemeResource DataGridCurrencyVisualPrimaryBrush}" StrokeThickness="1" Fill="Transparent"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False" Opacity="0"/>
|
||||
<Grid x:Name="FocusVisual" IsHitTestVisible="False" Opacity="0">
|
||||
<Rectangle Stroke="{ThemeResource DataGridCellFocusVisualPrimaryBrush}" StrokeThickness="2" Fill="Transparent"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False"/>
|
||||
<Rectangle Stroke="{ThemeResource DataGridCellFocusVisualSecondaryBrush}" StrokeThickness="1" Fill="Transparent"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False" Margin="2"/>
|
||||
</Grid>
|
||||
|
||||
<ContentPresenter
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
|
||||
<Rectangle x:Name="InvalidVisualElement" IsHitTestVisible="False" StrokeThickness="1" Stroke="{ThemeResource DataGridCellInvalidBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Opacity="0"/>
|
||||
|
||||
<Rectangle x:Name="RightGridLine" Grid.Column="1" VerticalAlignment="Stretch" Width="1" Fill="{ThemeResource DataGridFillerColumnGridLinesBrush}"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="localprimitives:DataGridColumnHeader">
|
||||
<Setter Property="Foreground" Value="{ThemeResource DataGridColumnHeaderForegroundBrush}"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="SeparatorBrush" Value="{ThemeResource GridLinesBrush}"/>
|
||||
<Setter Property="Padding" Value="12,0,0,0"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="MinHeight" Value="32"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="localprimitives:DataGridColumnHeader">
|
||||
<Grid x:Name="ColumnHeaderRoot">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridColumnHeaderBackgroundColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridColumnHeaderHoveredBackgroundColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridColumnHeaderPressedBackgroundColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="FocusStates">
|
||||
<VisualState x:Name="Unfocused"/>
|
||||
<VisualState x:Name="Focused">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="SortStates">
|
||||
<VisualState x:Name="Unsorted"/>
|
||||
<VisualState x:Name="SortAscending">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SortDescending">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="SortIcon.Glyph" Value="{ThemeResource SortIconDescending}"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Rectangle x:Name="BackgroundRectangle" Stretch="Fill" Fill="{ThemeResource DataGridColumnHeaderBackgroundBrush}" Grid.ColumnSpan="2"/>
|
||||
|
||||
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="{TemplateBinding Padding}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition MinWidth="32" Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
|
||||
<FontIcon Grid.Column="1" x:Name="SortIcon" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="{ThemeResource SortIconAscending}" FontSize="12"
|
||||
Foreground="{ThemeResource DataGridColumnHeaderForegroundBrush}" HorizontalAlignment="Center" VerticalAlignment="Center" Opacity="0"/>
|
||||
</Grid>
|
||||
<Rectangle x:Name="VerticalSeparator" Grid.Column="1" Width="1" VerticalAlignment="Stretch" Fill="{TemplateBinding SeparatorBrush}" Visibility="{TemplateBinding SeparatorVisibility}"/>
|
||||
|
||||
<Grid x:Name="FocusVisual" IsHitTestVisible="False" Opacity="0">
|
||||
<Rectangle x:Name="FocusVisualPrimary" Stroke="{ThemeResource DataGridCellFocusVisualPrimaryBrush}" StrokeThickness="2" Fill="Transparent"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False"/>
|
||||
<Rectangle x:Name="FocusVisualSecondary" Stroke="{ThemeResource DataGridCellFocusVisualSecondaryBrush}" StrokeThickness="1" Fill="Transparent"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False" Margin="2"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="local:DataGridRow">
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:DataGridRow">
|
||||
<localprimitives:DataGridFrozenGrid x:Name="RowRoot">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="NormalAlternatingRow"/>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource SystemListLowColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="NormalSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverUnfocusedSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredUnfocusedBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="UnfocusedSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedUnfocusedBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedUnfocusedBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ValidationStates">
|
||||
<VisualState x:Name="Valid"/>
|
||||
<VisualState x:Name="Invalid">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimation Storyboard.TargetName="InvalidVisualElement" Storyboard.TargetProperty="Opacity" Duration="0" To="0.4"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Rectangle x:Name="BackgroundRectangle" Grid.ColumnSpan="2" Fill="{ThemeResource SystemControlTransparentBrush}"/>
|
||||
<Rectangle x:Name="InvalidVisualElement" Grid.ColumnSpan="2" Opacity="0" Fill="{ThemeResource DataGridRowInvalidBrush}"/>
|
||||
|
||||
<localprimitives:DataGridRowHeader x:Name="RowHeader" Grid.RowSpan="3" localprimitives:DataGridFrozenGrid.IsFrozen="True"/>
|
||||
<localprimitives:DataGridCellsPresenter x:Name="CellsPresenter" Grid.Column="1"
|
||||
localprimitives:DataGridFrozenGrid.IsFrozen="True" MinHeight="32"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
<localprimitives:DataGridDetailsPresenter x:Name="DetailsPresenter" Grid.Row="1" Grid.Column="1"
|
||||
Background="{ThemeResource DataGridDetailsPresenterBackgroundBrush}"
|
||||
AutomationProperties.AccessibilityView="Raw"/>
|
||||
<Rectangle x:Name="BottomGridLine" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Stretch" Height="1"/>
|
||||
</localprimitives:DataGridFrozenGrid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="localprimitives:DataGridRowHeader">
|
||||
<Setter Property="Background" Value="{ThemeResource DataGridRowHeaderBackgroundBrush}"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="SeparatorBrush" Value="{ThemeResource GridLinesBrush}"/>
|
||||
<Setter Property="SeparatorVisibility" Value="Collapsed"/>
|
||||
<Setter Property="AutomationProperties.AccessibilityView" Value="Raw"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="localprimitives:DataGridRowHeader">
|
||||
<Grid x:Name="RowHeaderRoot">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="NormalCurrentRow">
|
||||
<!-- For optional row currency and editing glyphs
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/>
|
||||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/>
|
||||
</Storyboard>
|
||||
-->
|
||||
</VisualState>
|
||||
<VisualState x:Name="NormalSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedBackgroundOpacity}"/>
|
||||
<!-- For optional row currency and editing glyphs
|
||||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/>
|
||||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/>
|
||||
-->
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="NormalEditingRow">
|
||||
<!-- For optional row currency and editing glyphs
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/>
|
||||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/>
|
||||
</Storyboard>
|
||||
-->
|
||||
</VisualState>
|
||||
<VisualState x:Name="NormalCurrentRowSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource SystemListLowColor}"/>
|
||||
<!-- For optional row currency and editing glyphs
|
||||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/>
|
||||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/>
|
||||
-->
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverCurrentRow">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource SystemListLowColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverUnfocusedEditingRow">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource SystemListLowColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverEditingRow">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource SystemListLowColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverUnfocusedSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredUnfocusedBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverUnfocusedCurrentRowSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredUnfocusedBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverCurrentRowSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredBackgroundOpacity}"/>
|
||||
<!-- For optional row currency and editing glyphs
|
||||
<DoubleAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="1"/>
|
||||
<DoubleAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/>
|
||||
-->
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedHoveredBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="UnfocusedEditingRow">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedBackgroundColor}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="UnfocusedSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedUnfocusedBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedUnfocusedBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="UnfocusedCurrentRowSelected">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Duration="0" To="{ThemeResource DataGridRowSelectedUnfocusedBackgroundColor}"/>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="{ThemeResource DataGridRowSelectedUnfocusedBackgroundOpacity}"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ValidationStates">
|
||||
<VisualState x:Name="RowValid"/>
|
||||
<VisualState x:Name="RowInvalid">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="BackgroundRectangle" Storyboard.TargetProperty="Opacity" Duration="0" To="0"/>
|
||||
<DoubleAnimation Storyboard.TargetName="RowInvalidVisualElement" Storyboard.TargetProperty="Opacity" Duration="0" To="0.4"/>
|
||||
<!-- For optional row currency and editing glyphs
|
||||
<ColorAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Duration="0" To="#FFDC000C"/>
|
||||
<ColorAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Duration="0" To="#FFDC000C"/>
|
||||
<ColorAnimation Storyboard.TargetName="CurrentRowGlyph" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" Duration="0" To="#FFDC000C"/>
|
||||
<ColorAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Duration="0" To="#FFDC000C"/>
|
||||
<ColorAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Duration="0" To="#FFDC000C"/>
|
||||
<ColorAnimation Storyboard.TargetName="EditingRowGlyph" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" Duration="0" To="#FFDC000C"/>
|
||||
-->
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border BorderBrush="{TemplateBinding SeparatorBrush}" BorderThickness="0,0,1,0" Grid.RowSpan="3" Grid.ColumnSpan="2">
|
||||
<Grid Background="{TemplateBinding Background}">
|
||||
<Rectangle x:Name="RowInvalidVisualElement" Stretch="Fill" Fill="{ThemeResource DataGridRowInvalidBrush}" Opacity="0"/>
|
||||
<Rectangle x:Name="BackgroundRectangle" Stretch="Fill" Fill="Transparent"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Rectangle x:Name="HorizontalSeparator" Grid.ColumnSpan="2" Grid.Row="2" Height="1" Margin="1,0,1,0" HorizontalAlignment="Stretch" Fill="{TemplateBinding SeparatorBrush}" Visibility="{TemplateBinding SeparatorVisibility}"/>
|
||||
|
||||
<ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="1" Grid.RowSpan="2"/>
|
||||
|
||||
<!-- For optional row currency glyph
|
||||
<Path x:Name="CurrentRowGlyph" VerticalAlignment="Center" HorizontalAlignment="Center" Width="6" Height="10" Margin="8,0,8,0" Grid.RowSpan="2" Stretch="Fill" Opacity="0" Data="F1 M 511.047,352.682L 511.047,342.252L 517.145,347.467L 511.047,352.682 Z ">
|
||||
<Path.Fill>
|
||||
<LinearGradientBrush StartPoint="0,-.15" EndPoint="0,1.75">
|
||||
<GradientStop Color="#FF84E3FF" Offset="0"/>
|
||||
<GradientStop Color="#FF6ABFD8" Offset="0.5"/>
|
||||
<GradientStop Color="#FF5297AB" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
</Path.Fill>
|
||||
</Path>
|
||||
-->
|
||||
<!-- For optional row editing glyph
|
||||
<Path x:Name="EditingRowGlyph" VerticalAlignment="Center" HorizontalAlignment="Center" Width="6" Height="10" Margin="8,0,8,0" Grid.RowSpan="2" Stretch="Fill" Opacity="0" Data="F1 M 511.047,352.682L 511.047,342.252L 517.145,347.467L 511.047,352.682 Z ">
|
||||
<Path.Fill>
|
||||
<LinearGradientBrush StartPoint="0,-.15" EndPoint="0,1.75">
|
||||
<GradientStop Color="#FF84E3FF" Offset="0"/>
|
||||
<GradientStop Color="#FF6ABFD8" Offset="0.5"/>
|
||||
<GradientStop Color="#FF5297AB" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
</Path.Fill>
|
||||
</Path>
|
||||
-->
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="local:DataGridRowGroupHeader">
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="Foreground" Value="{ThemeResource DataGridRowGroupHeaderForegroundBrush}"/>
|
||||
<Setter Property="FontSize" Value="15"/>
|
||||
<Setter Property="MinHeight" Value="32"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:DataGridRowGroupHeader">
|
||||
<localprimitives:DataGridFrozenGrid x:Name="RowGroupHeaderRoot" Background="{ThemeResource DataGridRowGroupHeaderBackgroundBrush}" MinHeight="{TemplateBinding MinHeight}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RowGroupHeaderRoot.Background" Value="{ThemeResource DataGridRowGroupHeaderHoveredBackgroundBrush}"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RowGroupHeaderRoot.Background" Value="{ThemeResource DataGridRowGroupHeaderPressedBackgroundBrush}"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="CurrentStates">
|
||||
<VisualState x:Name="Regular"/>
|
||||
<VisualState x:Name="Current">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="CurrencyVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
|
||||
<DoubleAnimation Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="CurrentWithFocus">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="CurrencyVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
|
||||
<DoubleAnimation Storyboard.TargetName="FocusVisual" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<localprimitives:DataGridFrozenGrid.Resources>
|
||||
<ControlTemplate x:Key="ToggleButtonTemplate" TargetType="ToggleButton">
|
||||
<Grid Background="{TemplateBinding Background}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Arrow.Glyph" Value="{ThemeResource RowGroupHeaderIconClosed}"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Arrow.Glyph" Value="{ThemeResource RowGroupHeaderIconClosed}"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Arrow.Glyph" Value="{ThemeResource RowGroupHeaderIconClosed}"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Arrow.Glyph" Value="{ThemeResource RowGroupHeaderIconClosed}"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Checked"/>
|
||||
<VisualState x:Name="CheckedPointerOver"/>
|
||||
<VisualState x:Name="CheckedPressed"/>
|
||||
<VisualState x:Name="CheckedDisabled"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<FontIcon x:Name="Arrow" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="{ThemeResource RowGroupHeaderIconOpened}"
|
||||
FontSize="12" Foreground="{ThemeResource DataGridRowGroupHeaderForegroundBrush}"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</localprimitives:DataGridFrozenGrid.Resources>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Rectangle x:Name="IndentSpacer" Grid.Column="1"/>
|
||||
<ToggleButton x:Name="ExpanderButton" Grid.Column="2" Height="12" Width="12" Template="{StaticResource ToggleButtonTemplate}"
|
||||
IsTabStop="False" Margin="12,0,0,0" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}"/>
|
||||
|
||||
<StackPanel Grid.Column="3" Orientation="Horizontal" VerticalAlignment="Center" Margin="12,0,0,0">
|
||||
<TextBlock x:Name="PropertyNameElement" Margin="4,0,0,0" Visibility="{TemplateBinding PropertyNameVisibility}" Style="{ThemeResource BodyTextBlockStyle}" Foreground="{TemplateBinding Foreground}"/>
|
||||
<TextBlock x:Name="PropertyValueElement" Margin="4,0,0,0" Style="{ThemeResource BodyTextBlockStyle}" Foreground="{TemplateBinding Foreground}"/>
|
||||
<TextBlock x:Name="ItemCountElement" Margin="4,0,0,0" Visibility="{TemplateBinding ItemCountVisibility}" Style="{ThemeResource BodyTextBlockStyle}" Foreground="{TemplateBinding Foreground}"/>
|
||||
</StackPanel>
|
||||
|
||||
<Rectangle x:Name="CurrencyVisual" Grid.ColumnSpan="5"
|
||||
Stroke="{ThemeResource DataGridCurrencyVisualPrimaryBrush}" StrokeThickness="1" Fill="Transparent"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False" Opacity="0"/>
|
||||
<Grid x:Name="FocusVisual" Grid.ColumnSpan="5" IsHitTestVisible="False" Opacity="0">
|
||||
<Rectangle Stroke="{ThemeResource DataGridCellFocusVisualPrimaryBrush}" StrokeThickness="2" Fill="Transparent"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False"/>
|
||||
<Rectangle Stroke="{ThemeResource DataGridCellFocusVisualSecondaryBrush}" StrokeThickness="1" Fill="Transparent"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="False" Margin="2"/>
|
||||
</Grid>
|
||||
|
||||
<localprimitives:DataGridRowHeader x:Name="RowHeader" Grid.RowSpan="2" localprimitives:DataGridFrozenGrid.IsFrozen="True"/>
|
||||
|
||||
<Rectangle x:Name="BottomGridLine" Grid.ColumnSpan="5" Height="1" Grid.Row="1"/>
|
||||
</localprimitives:DataGridFrozenGrid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
|
@ -0,0 +1,61 @@
|
|||
// 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>
|
||||
/// Provides data for the <see cref="E:Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.AutoGeneratingColumn" /> event.
|
||||
/// </summary>
|
||||
public class DataGridAutoGeneratingColumnEventArgs : CancelEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridAutoGeneratingColumnEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">
|
||||
/// The name of the property bound to the generated column.
|
||||
/// </param>
|
||||
/// <param name="propertyType">
|
||||
/// The <see cref="T:System.Type" /> of the property bound to the generated column.
|
||||
/// </param>
|
||||
/// <param name="column">
|
||||
/// The generated column.
|
||||
/// </param>
|
||||
public DataGridAutoGeneratingColumnEventArgs(string propertyName, Type propertyType, DataGridColumn column)
|
||||
{
|
||||
this.Column = column;
|
||||
this.PropertyName = propertyName;
|
||||
this.PropertyType = propertyType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the generated column.
|
||||
/// </summary>
|
||||
public DataGridColumn Column
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the property bound to the generated column.
|
||||
/// </summary>
|
||||
public string PropertyName
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="T:System.Type"/> of the property bound to the generated column.
|
||||
/// </summary>
|
||||
public Type PropertyType
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// 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.ComponentModel;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data for the <see cref="E:Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.BeginningEdit" /> event.
|
||||
/// </summary>
|
||||
public class DataGridBeginningEditEventArgs : CancelEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridBeginningEditEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="column">
|
||||
/// The column that contains the cell to be edited.
|
||||
/// </param>
|
||||
/// <param name="row">
|
||||
/// The row that contains the cell to be edited.
|
||||
/// </param>
|
||||
/// <param name="editingEventArgs">
|
||||
/// Information about the user gesture that caused the cell to enter edit mode.
|
||||
/// </param>
|
||||
public DataGridBeginningEditEventArgs(
|
||||
DataGridColumn column,
|
||||
DataGridRow row,
|
||||
RoutedEventArgs editingEventArgs)
|
||||
{
|
||||
this.Column = column;
|
||||
this.Row = row;
|
||||
this.EditingEventArgs = editingEventArgs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the column that contains the cell to be edited.
|
||||
/// </summary>
|
||||
public DataGridColumn Column
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the user gesture that caused the cell to enter edit mode.
|
||||
/// </summary>
|
||||
public RoutedEventArgs EditingEventArgs
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row that contains the cell to be edited.
|
||||
/// </summary>
|
||||
public DataGridRow Row
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
// 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 Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Microsoft.Toolkit.Uwp.UI.Data.Utilities;
|
||||
using Microsoft.Toolkit.Uwp.Utilities;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="DataGrid"/> column that can
|
||||
/// bind to a property in the grid's data source.
|
||||
/// </summary>
|
||||
[StyleTypedProperty(Property = "ElementStyle", StyleTargetType = typeof(FrameworkElement))]
|
||||
[StyleTypedProperty(Property = "EditingElementStyle", StyleTargetType = typeof(FrameworkElement))]
|
||||
public abstract class DataGridBoundColumn : DataGridColumn
|
||||
{
|
||||
private Binding _binding;
|
||||
private Style _elementStyle;
|
||||
private Style _editingElementStyle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the binding that associates the column with a property in the data source.
|
||||
/// </summary>
|
||||
public virtual Binding Binding
|
||||
{
|
||||
get
|
||||
{
|
||||
return _binding;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_binding != value)
|
||||
{
|
||||
if (this.OwningGrid != null && !this.OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/))
|
||||
{
|
||||
// Edited value couldn't be committed, so we force a CancelEdit
|
||||
this.OwningGrid.CancelEdit(DataGridEditingUnit.Row, false /*raiseEvents*/);
|
||||
}
|
||||
|
||||
_binding = value;
|
||||
|
||||
if (_binding != null)
|
||||
{
|
||||
// Force the TwoWay binding mode if there is a Path present. TwoWay binding requires a Path.
|
||||
if (_binding.Path != null && !string.IsNullOrEmpty(_binding.Path.Path))
|
||||
{
|
||||
_binding.Mode = BindingMode.TwoWay;
|
||||
}
|
||||
|
||||
if (_binding.Converter == null)
|
||||
{
|
||||
_binding.Converter = new DataGridValueConverter();
|
||||
}
|
||||
|
||||
// Setup the binding for validation
|
||||
// Todo: WinUI3, can this be reenabled now?
|
||||
// _binding.ValidatesOnDataErrors = true;
|
||||
// _binding.ValidatesOnExceptions = true;
|
||||
// _binding.NotifyOnValidationError = true;
|
||||
_binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
|
||||
|
||||
// Apply the new Binding to existing rows in the DataGrid
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
// TODO: We want to clear the Bindings if Binding is set to null
|
||||
// but there's no way to do that right now. Revisit this if UWP
|
||||
// implements the equivalent of BindingOperations.ClearBinding.
|
||||
this.OwningGrid.OnColumnBindingChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
this.RemoveEditingElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the binding that will be used to get or set cell content for the clipboard.
|
||||
/// If the base ClipboardContentBinding is not explicitly set, this will return the value of Binding.
|
||||
/// </summary>
|
||||
public override Binding ClipboardContentBinding
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.ClipboardContentBinding ?? this.Binding;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
base.ClipboardContentBinding = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the style that is used when rendering the element that the column displays for a cell in editing mode.
|
||||
/// </summary>
|
||||
public Style EditingElementStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
return _editingElementStyle;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_editingElementStyle != value)
|
||||
{
|
||||
_editingElementStyle = value;
|
||||
|
||||
// We choose not to update the elements already editing in the Grid here.
|
||||
// They will get the EditingElementStyle next time they go into edit mode.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the style that is used when rendering the element that the column displays for a cell that is not in editing mode.
|
||||
/// </summary>
|
||||
public Style ElementStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
return _elementStyle;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_elementStyle != value)
|
||||
{
|
||||
_elementStyle = value;
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
this.OwningGrid.OnColumnElementStyleChanged(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal DependencyProperty BindingTarget { get; set; }
|
||||
|
||||
internal override List<string> CreateBindingPaths()
|
||||
{
|
||||
if (this.Binding != null && this.Binding.Path != null)
|
||||
{
|
||||
return new List<string>() { this.Binding.Path.Path };
|
||||
}
|
||||
|
||||
return base.CreateBindingPaths();
|
||||
}
|
||||
|
||||
internal override List<BindingInfo> CreateBindings(FrameworkElement element, object dataItem, bool twoWay)
|
||||
{
|
||||
BindingInfo bindingData = new BindingInfo();
|
||||
if (twoWay && this.BindingTarget != null)
|
||||
{
|
||||
bindingData.BindingExpression = element.GetBindingExpression(this.BindingTarget);
|
||||
if (bindingData.BindingExpression != null)
|
||||
{
|
||||
bindingData.BindingTarget = this.BindingTarget;
|
||||
bindingData.Element = element;
|
||||
return new List<BindingInfo> { bindingData };
|
||||
}
|
||||
}
|
||||
|
||||
foreach (DependencyProperty bindingTarget in element.GetDependencyProperties(false))
|
||||
{
|
||||
bindingData.BindingExpression = element.GetBindingExpression(bindingTarget);
|
||||
if (bindingData.BindingExpression != null
|
||||
&& bindingData.BindingExpression.ParentBinding == this.Binding)
|
||||
{
|
||||
this.BindingTarget = bindingTarget;
|
||||
bindingData.BindingTarget = this.BindingTarget;
|
||||
bindingData.Element = element;
|
||||
return new List<BindingInfo> { bindingData };
|
||||
}
|
||||
}
|
||||
|
||||
return base.CreateBindings(element, dataItem, twoWay);
|
||||
}
|
||||
|
||||
#if FEATURE_ICOLLECTIONVIEW_SORT
|
||||
internal override string GetSortPropertyName()
|
||||
{
|
||||
if (string.IsNullOrEmpty(this.SortMemberPath) && this.Binding != null && this.Binding.Path != null)
|
||||
{
|
||||
return this.Binding.Path.Path;
|
||||
}
|
||||
|
||||
return this.SortMemberPath;
|
||||
}
|
||||
#endif
|
||||
|
||||
internal void SetHeaderFromBinding()
|
||||
{
|
||||
if (this.OwningGrid != null && this.OwningGrid.DataConnection.DataType != null &&
|
||||
this.Header == null && this.Binding != null && this.Binding.Path != null)
|
||||
{
|
||||
string header = this.OwningGrid.DataConnection.DataType.GetDisplayName(this.Binding.Path.Path);
|
||||
if (header != null)
|
||||
{
|
||||
this.Header = header;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,470 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.Utilities;
|
||||
using Microsoft.Toolkit.Uwp.UI.Utilities;
|
||||
using Windows.Devices.Input;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an individual <see cref="DataGrid"/> cell.
|
||||
/// </summary>
|
||||
[TemplatePart(Name = DATAGRIDCELL_elementRightGridLine, Type = typeof(Rectangle))]
|
||||
|
||||
[TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = VisualStates.StatePointerOver, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = VisualStates.StateUnselected, GroupName = VisualStates.GroupSelection)]
|
||||
[TemplateVisualState(Name = VisualStates.StateSelected, GroupName = VisualStates.GroupSelection)]
|
||||
[TemplateVisualState(Name = VisualStates.StateRegular, GroupName = VisualStates.GroupCurrent)]
|
||||
[TemplateVisualState(Name = VisualStates.StateCurrent, GroupName = VisualStates.GroupCurrent)]
|
||||
[TemplateVisualState(Name = VisualStates.StateCurrentWithFocus, GroupName = VisualStates.GroupCurrent)]
|
||||
[TemplateVisualState(Name = VisualStates.StateDisplay, GroupName = VisualStates.GroupInteraction)]
|
||||
[TemplateVisualState(Name = VisualStates.StateEditing, GroupName = VisualStates.GroupInteraction)]
|
||||
[TemplateVisualState(Name = VisualStates.StateInvalid, GroupName = VisualStates.GroupValidation)]
|
||||
[TemplateVisualState(Name = VisualStates.StateValid, GroupName = VisualStates.GroupValidation)]
|
||||
public sealed partial class DataGridCell : ContentControl
|
||||
{
|
||||
private const string DATAGRIDCELL_elementRightGridLine = "RightGridLine";
|
||||
|
||||
private Rectangle _rightGridLine;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridCell"/> class.
|
||||
/// </summary>
|
||||
public DataGridCell()
|
||||
{
|
||||
this.IsTapEnabled = true;
|
||||
this.AddHandler(UIElement.TappedEvent, new TappedEventHandler(DataGridCell_PointerTapped), true /*handledEventsToo*/);
|
||||
|
||||
this.PointerCanceled += new PointerEventHandler(DataGridCell_PointerCanceled);
|
||||
this.PointerCaptureLost += new PointerEventHandler(DataGridCell_PointerCaptureLost);
|
||||
this.PointerPressed += new PointerEventHandler(DataGridCell_PointerPressed);
|
||||
this.PointerReleased += new PointerEventHandler(DataGridCell_PointerReleased);
|
||||
this.PointerEntered += new PointerEventHandler(DataGridCell_PointerEntered);
|
||||
this.PointerExited += new PointerEventHandler(DataGridCell_PointerExited);
|
||||
this.PointerMoved += new PointerEventHandler(DataGridCell_PointerMoved);
|
||||
|
||||
DefaultStyleKey = typeof(DataGridCell);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the data in a cell is valid.
|
||||
/// </summary>
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return (bool)GetValue(IsValidProperty);
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
this.SetValueNoCallback(IsValidProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the IsValid dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsValidProperty =
|
||||
DependencyProperty.Register(
|
||||
"IsValid",
|
||||
typeof(bool),
|
||||
typeof(DataGridCell),
|
||||
new PropertyMetadata(true, OnIsValidPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// IsValidProperty property changed handler.
|
||||
/// </summary>
|
||||
/// <param name="d">DataGridCell that changed its IsValid.</param>
|
||||
/// <param name="e">DependencyPropertyChangedEventArgs.</param>
|
||||
private static void OnIsValidPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
DataGridCell dataGridCell = d as DataGridCell;
|
||||
if (!dataGridCell.IsHandlerSuspended(e.Property))
|
||||
{
|
||||
dataGridCell.SetValueNoCallback(DataGridCell.IsValidProperty, e.OldValue);
|
||||
throw DataGridError.DataGrid.UnderlyingPropertyIsReadOnly("IsValid");
|
||||
}
|
||||
}
|
||||
|
||||
internal double ActualRightGridLineWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_rightGridLine != null)
|
||||
{
|
||||
return _rightGridLine.ActualWidth;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal int ColumnIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningColumn == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.OwningColumn.Index;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsCurrent
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(this.OwningGrid != null && this.OwningColumn != null && this.OwningRow != null, "Expected non-null owning DataGrid, DataGridColumn and DataGridRow.");
|
||||
|
||||
return this.OwningGrid.CurrentColumnIndex == this.OwningColumn.Index &&
|
||||
this.OwningGrid.CurrentSlot == this.OwningRow.Slot;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsPointerOver
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.InteractionInfo != null && this.InteractionInfo.IsPointerOver;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value && this.InteractionInfo == null)
|
||||
{
|
||||
this.InteractionInfo = new DataGridInteractionInfo();
|
||||
}
|
||||
|
||||
if (this.InteractionInfo != null)
|
||||
{
|
||||
this.InteractionInfo.IsPointerOver = value;
|
||||
}
|
||||
|
||||
ApplyCellState(true /*animate*/);
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridColumn OwningColumn
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal DataGrid OwningGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningRow != null && this.OwningRow.OwningGrid != null)
|
||||
{
|
||||
return this.OwningRow.OwningGrid;
|
||||
}
|
||||
|
||||
if (this.OwningColumn != null)
|
||||
{
|
||||
return this.OwningColumn.OwningGrid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridRow OwningRow
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal int RowIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningRow == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.OwningRow.Index;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridInteractionInfo InteractionInfo
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private bool IsEdited
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(this.OwningGrid != null, "Expected non-null owning DataGrid.");
|
||||
|
||||
return this.OwningGrid.EditingRow == this.OwningRow &&
|
||||
this.OwningGrid.EditingColumnIndex == this.ColumnIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the visual tree for the row header when a new template is applied.
|
||||
/// </summary>
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
ApplyCellState(false /*animate*/);
|
||||
|
||||
_rightGridLine = GetTemplateChild(DATAGRIDCELL_elementRightGridLine) as Rectangle;
|
||||
if (_rightGridLine != null && this.OwningColumn == null)
|
||||
{
|
||||
// Turn off the right GridLine for filler cells
|
||||
_rightGridLine.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureGridLine(null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
|
||||
/// </summary>
|
||||
/// <returns>An automation peer for this <see cref="DataGridCell"/>.</returns>
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
if (this.OwningGrid != null &&
|
||||
this.OwningColumn != null &&
|
||||
this.OwningColumn != this.OwningGrid.ColumnsInternal.FillerColumn)
|
||||
{
|
||||
return new DataGridCellAutomationPeer(this);
|
||||
}
|
||||
|
||||
return base.OnCreateAutomationPeer();
|
||||
}
|
||||
|
||||
internal void ApplyCellState(bool animate)
|
||||
{
|
||||
if (this.OwningGrid == null || this.OwningColumn == null || this.OwningRow == null || this.OwningRow.Visibility == Visibility.Collapsed || this.OwningRow.Slot == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// CommonStates
|
||||
if (this.IsPointerOver)
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StatePointerOver, VisualStates.StateNormal);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateNormal);
|
||||
}
|
||||
|
||||
// SelectionStates
|
||||
if (this.OwningRow.IsSelected)
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateSelected, VisualStates.StateUnselected);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateUnselected);
|
||||
}
|
||||
|
||||
// CurrentStates
|
||||
if (this.IsCurrent && !this.OwningGrid.ColumnHeaderHasFocus)
|
||||
{
|
||||
if (this.OwningGrid.ContainsFocus)
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateCurrentWithFocus, VisualStates.StateCurrent, VisualStates.StateRegular);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateCurrent, VisualStates.StateRegular);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateRegular);
|
||||
}
|
||||
|
||||
// Interaction states
|
||||
if (this.IsEdited)
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateEditing, VisualStates.StateDisplay);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateDisplay);
|
||||
}
|
||||
|
||||
// Validation states
|
||||
if (this.IsValid)
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateValid);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, animate, VisualStates.StateInvalid, VisualStates.StateValid);
|
||||
}
|
||||
}
|
||||
|
||||
// Makes sure the right gridline has the proper stroke and visibility. If lastVisibleColumn is specified, the
|
||||
// right gridline will be collapsed if this cell belongs to the lastVisibileColumn and there is no filler column
|
||||
internal void EnsureGridLine(DataGridColumn lastVisibleColumn)
|
||||
{
|
||||
if (this.OwningGrid != null && _rightGridLine != null)
|
||||
{
|
||||
if (!(this.OwningColumn is DataGridFillerColumn) && this.OwningGrid.VerticalGridLinesBrush != null && this.OwningGrid.VerticalGridLinesBrush != _rightGridLine.Fill)
|
||||
{
|
||||
_rightGridLine.Fill = this.OwningGrid.VerticalGridLinesBrush;
|
||||
}
|
||||
|
||||
Visibility newVisibility =
|
||||
(this.OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.Vertical || this.OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.All) &&
|
||||
(this.OwningGrid.ColumnsInternal.FillerColumn.IsActive || this.OwningColumn != lastVisibleColumn)
|
||||
? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
if (newVisibility != _rightGridLine.Visibility)
|
||||
{
|
||||
_rightGridLine.Visibility = newVisibility;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the correct Style is applied to this object.
|
||||
/// </summary>
|
||||
/// <param name="previousStyle">Caller's previous associated Style</param>
|
||||
internal void EnsureStyle(Style previousStyle)
|
||||
{
|
||||
if (this.Style != null &&
|
||||
(this.OwningColumn == null || this.Style != this.OwningColumn.CellStyle) &&
|
||||
(this.OwningGrid == null || this.Style != this.OwningGrid.CellStyle) &&
|
||||
this.Style != previousStyle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Style style = null;
|
||||
if (this.OwningColumn != null)
|
||||
{
|
||||
style = this.OwningColumn.CellStyle;
|
||||
}
|
||||
|
||||
if (style == null && this.OwningGrid != null)
|
||||
{
|
||||
style = this.OwningGrid.CellStyle;
|
||||
}
|
||||
|
||||
this.SetStyleWithType(style);
|
||||
}
|
||||
|
||||
internal void Recycle()
|
||||
{
|
||||
this.InteractionInfo = null;
|
||||
}
|
||||
|
||||
private void CancelPointer(PointerRoutedEventArgs e)
|
||||
{
|
||||
if (this.InteractionInfo != null && this.InteractionInfo.CapturedPointerId == e.Pointer.PointerId)
|
||||
{
|
||||
this.InteractionInfo.CapturedPointerId = 0u;
|
||||
}
|
||||
|
||||
this.IsPointerOver = false;
|
||||
}
|
||||
|
||||
private void DataGridCell_PointerCanceled(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
CancelPointer(e);
|
||||
}
|
||||
|
||||
private void DataGridCell_PointerCaptureLost(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
CancelPointer(e);
|
||||
}
|
||||
|
||||
private void DataGridCell_PointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (e.Pointer.PointerDeviceType == PointerDeviceType.Touch &&
|
||||
this.OwningGrid != null &&
|
||||
this.OwningGrid.AllowsManipulation &&
|
||||
(this.InteractionInfo == null || this.InteractionInfo.CapturedPointerId == 0u) &&
|
||||
this.CapturePointer(e.Pointer))
|
||||
{
|
||||
if (this.InteractionInfo == null)
|
||||
{
|
||||
this.InteractionInfo = new DataGridInteractionInfo();
|
||||
}
|
||||
|
||||
this.InteractionInfo.CapturedPointerId = e.Pointer.PointerId;
|
||||
}
|
||||
}
|
||||
|
||||
private void DataGridCell_PointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (this.InteractionInfo != null && this.InteractionInfo.CapturedPointerId == e.Pointer.PointerId)
|
||||
{
|
||||
ReleasePointerCapture(e.Pointer);
|
||||
}
|
||||
}
|
||||
|
||||
private void DataGridCell_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateIsPointerOver(true);
|
||||
}
|
||||
|
||||
private void DataGridCell_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateIsPointerOver(false);
|
||||
}
|
||||
|
||||
private void DataGridCell_PointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateIsPointerOver(true);
|
||||
}
|
||||
|
||||
private void DataGridCell_PointerTapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
// OwningGrid is null for TopLeftHeaderCell and TopRightHeaderCell because they have no OwningRow
|
||||
if (this.OwningGrid != null && !this.OwningGrid.HasColumnUserInteraction)
|
||||
{
|
||||
if (!e.Handled && this.OwningGrid.IsTabStop)
|
||||
{
|
||||
bool success = this.OwningGrid.Focus(FocusState.Programmatic);
|
||||
Debug.Assert(success, "Expected successful focus change.");
|
||||
}
|
||||
|
||||
if (this.OwningRow != null)
|
||||
{
|
||||
Debug.Assert(sender is DataGridCell, "Expected sender is DataGridCell.");
|
||||
Debug.Assert(sender as ContentControl == this, "Expected sender is this.");
|
||||
e.Handled = this.OwningGrid.UpdateStateOnTapped(e, this.ColumnIndex, this.OwningRow.Slot, !e.Handled /*allowEdit*/);
|
||||
this.OwningGrid.UpdatedStateOnTapped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateIsPointerOver(bool isPointerOver)
|
||||
{
|
||||
if (this.InteractionInfo != null && this.InteractionInfo.CapturedPointerId != 0u)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.IsPointerOver = isPointerOver;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
internal class DataGridCellCollection
|
||||
{
|
||||
private List<DataGridCell> _cells;
|
||||
private DataGridRow _owningRow;
|
||||
|
||||
internal event EventHandler<DataGridCellEventArgs> CellAdded;
|
||||
|
||||
internal event EventHandler<DataGridCellEventArgs> CellRemoved;
|
||||
|
||||
public DataGridCellCollection(DataGridRow owningRow)
|
||||
{
|
||||
_owningRow = owningRow;
|
||||
_cells = new List<DataGridCell>();
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cells.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return _cells.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Insert(int cellIndex, DataGridCell cell)
|
||||
{
|
||||
Debug.Assert(cellIndex >= 0 && cellIndex <= _cells.Count, "Expected cellIndex between 0 and _cells.Count inclusive.");
|
||||
Debug.Assert(cell != null, "Expected non-null cell.");
|
||||
|
||||
cell.OwningRow = _owningRow;
|
||||
_cells.Insert(cellIndex, cell);
|
||||
|
||||
if (CellAdded != null)
|
||||
{
|
||||
CellAdded(this, new DataGridCellEventArgs(cell));
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int cellIndex)
|
||||
{
|
||||
DataGridCell dataGridCell = _cells[cellIndex];
|
||||
_cells.RemoveAt(cellIndex);
|
||||
dataGridCell.OwningRow = null;
|
||||
if (CellRemoved != null)
|
||||
{
|
||||
CellRemoved(this, new DataGridCellEventArgs(dataGridCell));
|
||||
}
|
||||
}
|
||||
|
||||
public DataGridCell this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= _cells.Count)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueMustBeBetween("index", "Index", 0, true, _cells.Count, false);
|
||||
}
|
||||
|
||||
return _cells[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// 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.Globalization;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals
|
||||
{
|
||||
internal class DataGridCellCoordinates
|
||||
{
|
||||
public DataGridCellCoordinates(int columnIndex, int slot)
|
||||
{
|
||||
this.ColumnIndex = columnIndex;
|
||||
this.Slot = slot;
|
||||
}
|
||||
|
||||
public DataGridCellCoordinates(DataGridCellCoordinates dataGridCellCoordinates)
|
||||
: this(dataGridCellCoordinates.ColumnIndex, dataGridCellCoordinates.Slot)
|
||||
{
|
||||
}
|
||||
|
||||
public int ColumnIndex
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int Slot
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override bool Equals(object o)
|
||||
{
|
||||
DataGridCellCoordinates dataGridCellCoordinates = o as DataGridCellCoordinates;
|
||||
if (dataGridCellCoordinates != null)
|
||||
{
|
||||
return dataGridCellCoordinates.ColumnIndex == this.ColumnIndex && dataGridCellCoordinates.Slot == this.Slot;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Avoiding build warning CS0659: 'DataGridCellCoordinates' overrides Object.Equals(object o) but does not override Object.GetHashCode()
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public override string ToString()
|
||||
{
|
||||
return "DataGridCellCoordinates {ColumnIndex = " + this.ColumnIndex.ToString(CultureInfo.CurrentCulture) +
|
||||
", Slot = " + this.Slot.ToString(CultureInfo.CurrentCulture) + "}";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// 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>
|
||||
/// Provides information just after a cell has exited editing mode.
|
||||
/// </summary>
|
||||
public class DataGridCellEditEndedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridCellEditEndedEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="column">The column of the cell that has just exited edit mode.</param>
|
||||
/// <param name="row">The row container of the cell container that has just exited edit mode.</param>
|
||||
/// <param name="editAction">The editing action that has been taken.</param>
|
||||
public DataGridCellEditEndedEventArgs(DataGridColumn column, DataGridRow row, DataGridEditAction editAction)
|
||||
{
|
||||
this.Column = column;
|
||||
this.Row = row;
|
||||
this.EditAction = editAction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the column of the cell that has just exited edit mode.
|
||||
/// </summary>
|
||||
public DataGridColumn Column
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the edit action taken when leaving edit mode.
|
||||
/// </summary>
|
||||
public DataGridEditAction EditAction
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row container of the cell container that has just exited edit mode.
|
||||
/// </summary>
|
||||
public DataGridRow Row
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// 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.ComponentModel;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides information just before a cell exits editing mode.
|
||||
/// </summary>
|
||||
public class DataGridCellEditEndingEventArgs : CancelEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridCellEditEndingEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="column">The column of the cell that is about to exit edit mode.</param>
|
||||
/// <param name="row">The row container of the cell container that is about to exit edit mode.</param>
|
||||
/// <param name="editingElement">The editing element within the cell.</param>
|
||||
/// <param name="editAction">The editing action that will be taken.</param>
|
||||
public DataGridCellEditEndingEventArgs(
|
||||
DataGridColumn column,
|
||||
DataGridRow row,
|
||||
FrameworkElement editingElement,
|
||||
DataGridEditAction editAction)
|
||||
{
|
||||
this.Column = column;
|
||||
this.Row = row;
|
||||
this.EditingElement = editingElement;
|
||||
this.EditAction = editAction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the column of the cell that is about to exit edit mode.
|
||||
/// </summary>
|
||||
public DataGridColumn Column
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the edit action to take when leaving edit mode.
|
||||
/// </summary>
|
||||
public DataGridEditAction EditAction
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the editing element within the cell.
|
||||
/// </summary>
|
||||
public FrameworkElement EditingElement
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row container of the cell container that is about to exit edit mode.
|
||||
/// </summary>
|
||||
public DataGridRow Row
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// 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.Diagnostics;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
internal class DataGridCellEventArgs : EventArgs
|
||||
{
|
||||
internal DataGridCellEventArgs(DataGridCell dataGridCell)
|
||||
{
|
||||
Debug.Assert(dataGridCell != null, "Expected non-null dataGridCell parameter.");
|
||||
|
||||
this.Cell = dataGridCell;
|
||||
}
|
||||
|
||||
internal DataGridCell Cell
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.Utilities;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Used within the template of a <see cref="DataGrid"/>
|
||||
/// to specify the location in the control's visual tree where the cells are to be added.
|
||||
/// </summary>
|
||||
public sealed class DataGridCellsPresenter : Panel
|
||||
{
|
||||
private double _fillerLeftEdge;
|
||||
|
||||
// The desired height needs to be cached due to column virtualization; otherwise, the cells
|
||||
// would grow and shrink as the DataGrid scrolls horizontally
|
||||
private double DesiredHeight
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private DataGrid OwningGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningRow != null)
|
||||
{
|
||||
return this.OwningRow.OwningGrid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridRow OwningRow
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arranges the content of the <see cref="T:System.Windows.Controls.Primitives.DataGridCellsPresenter"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The actual size used by the <see cref="T:System.Windows.Controls.Primitives.DataGridCellsPresenter"/>.
|
||||
/// </returns>
|
||||
/// <param name="finalSize">
|
||||
/// The final area within the parent that this element should use to arrange itself and its children.
|
||||
/// </param>
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (this.OwningGrid == null)
|
||||
{
|
||||
return base.ArrangeOverride(finalSize);
|
||||
}
|
||||
|
||||
if (this.OwningGrid.AutoSizingColumns)
|
||||
{
|
||||
// When we initially load an auto-column, we have to wait for all the rows to be measured
|
||||
// before we know its final desired size. We need to trigger a new round of measures now
|
||||
// that the final sizes have been calculated.
|
||||
this.OwningGrid.AutoSizingColumns = false;
|
||||
return base.ArrangeOverride(finalSize);
|
||||
}
|
||||
|
||||
double frozenLeftEdge = 0;
|
||||
double scrollingLeftEdge = -this.OwningGrid.HorizontalOffset;
|
||||
|
||||
double cellLeftEdge;
|
||||
foreach (DataGridColumn column in this.OwningGrid.ColumnsInternal.GetVisibleColumns())
|
||||
{
|
||||
DataGridCell cell = this.OwningRow.Cells[column.Index];
|
||||
Debug.Assert(cell.OwningColumn == column, "Expected column owner.");
|
||||
Debug.Assert(column.IsVisible, "Expected visible column.");
|
||||
|
||||
if (column.IsFrozen)
|
||||
{
|
||||
cellLeftEdge = frozenLeftEdge;
|
||||
|
||||
// This can happen before or after clipping because frozen cells aren't clipped
|
||||
frozenLeftEdge += column.ActualWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
cellLeftEdge = scrollingLeftEdge;
|
||||
}
|
||||
|
||||
if (cell.Visibility == Visibility.Visible)
|
||||
{
|
||||
cell.Arrange(new Rect(cellLeftEdge, 0, column.LayoutRoundedWidth, finalSize.Height));
|
||||
EnsureCellClip(cell, column.ActualWidth, finalSize.Height, frozenLeftEdge, scrollingLeftEdge);
|
||||
}
|
||||
|
||||
scrollingLeftEdge += column.ActualWidth;
|
||||
column.IsInitialDesiredWidthDetermined = true;
|
||||
}
|
||||
|
||||
_fillerLeftEdge = scrollingLeftEdge;
|
||||
|
||||
// FillerColumn.Width == 0 when the filler column is not active
|
||||
this.OwningRow.FillerCell.Arrange(new Rect(_fillerLeftEdge, 0, this.OwningGrid.ColumnsInternal.FillerColumn.FillerWidth, finalSize.Height));
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
private static void EnsureCellClip(DataGridCell cell, double width, double height, double frozenLeftEdge, double cellLeftEdge)
|
||||
{
|
||||
// Clip the cell only if it's scrolled under frozen columns. Unfortunately, we need to clip in this case
|
||||
// because cells could be transparent
|
||||
if (!cell.OwningColumn.IsFrozen && frozenLeftEdge > cellLeftEdge)
|
||||
{
|
||||
RectangleGeometry rg = new RectangleGeometry();
|
||||
double xClip = Math.Round(Math.Min(width, frozenLeftEdge - cellLeftEdge));
|
||||
rg.Rect = new Rect(xClip, 0, Math.Max(0, width - xClip), height);
|
||||
cell.Clip = rg;
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.Clip = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureCellDisplay(DataGridCell cell, bool displayColumn)
|
||||
{
|
||||
if (cell.IsCurrent)
|
||||
{
|
||||
if (displayColumn)
|
||||
{
|
||||
cell.Visibility = Visibility.Visible;
|
||||
cell.Clip = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clip
|
||||
RectangleGeometry rg = new RectangleGeometry();
|
||||
rg.Rect = Rect.Empty;
|
||||
cell.Clip = rg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.Visibility = displayColumn ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
internal void EnsureFillerVisibility()
|
||||
{
|
||||
DataGridFillerColumn fillerColumn = this.OwningGrid.ColumnsInternal.FillerColumn;
|
||||
Visibility newVisibility = fillerColumn.IsActive ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (this.OwningRow.FillerCell.Visibility != newVisibility)
|
||||
{
|
||||
this.OwningRow.FillerCell.Visibility = newVisibility;
|
||||
if (newVisibility == Visibility.Visible)
|
||||
{
|
||||
this.OwningRow.FillerCell.Arrange(new Rect(_fillerLeftEdge, 0, fillerColumn.FillerWidth, this.ActualHeight));
|
||||
}
|
||||
}
|
||||
|
||||
// This must be done after the Filler visibility is determined. This also must be done
|
||||
// regardless of whether or not the filler visibility actually changed values because
|
||||
// we could scroll in a cell that didn't have EnsureGridLine called yet
|
||||
DataGridColumn lastVisibleColumn = this.OwningGrid.ColumnsInternal.LastVisibleColumn;
|
||||
if (lastVisibleColumn != null)
|
||||
{
|
||||
DataGridCell cell = this.OwningRow.Cells[lastVisibleColumn.Index];
|
||||
cell.EnsureGridLine(lastVisibleColumn);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures the children of a <see cref="T:System.Windows.Controls.Primitives.DataGridCellsPresenter"/> to
|
||||
/// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)"/> pass.
|
||||
/// </summary>
|
||||
/// <param name="availableSize">
|
||||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The size that the <see cref="T:System.Windows.Controls.Primitives.DataGridCellsPresenter"/> determines it needs during layout, based on its calculations of child object allocated sizes.
|
||||
/// </returns>
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (this.OwningGrid == null)
|
||||
{
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
bool autoSizeHeight;
|
||||
double measureHeight;
|
||||
if (double.IsNaN(this.OwningGrid.RowHeight))
|
||||
{
|
||||
// No explicit height values were set so we can autosize
|
||||
autoSizeHeight = true;
|
||||
measureHeight = double.PositiveInfinity;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.DesiredHeight = this.OwningGrid.RowHeight;
|
||||
measureHeight = this.DesiredHeight;
|
||||
autoSizeHeight = false;
|
||||
}
|
||||
|
||||
double frozenLeftEdge = 0;
|
||||
double totalDisplayWidth = 0;
|
||||
double scrollingLeftEdge = -this.OwningGrid.HorizontalOffset;
|
||||
this.OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
|
||||
DataGridColumn lastVisibleColumn = this.OwningGrid.ColumnsInternal.LastVisibleColumn;
|
||||
foreach (DataGridColumn column in this.OwningGrid.ColumnsInternal.GetVisibleColumns())
|
||||
{
|
||||
DataGridCell cell = this.OwningRow.Cells[column.Index];
|
||||
|
||||
// Measure the entire first row to make the horizontal scrollbar more accurate
|
||||
bool shouldDisplayCell = ShouldDisplayCell(column, frozenLeftEdge, scrollingLeftEdge) || this.OwningRow.Index == 0;
|
||||
EnsureCellDisplay(cell, shouldDisplayCell);
|
||||
if (shouldDisplayCell)
|
||||
{
|
||||
DataGridLength columnWidth = column.Width;
|
||||
bool autoGrowWidth = columnWidth.IsSizeToCells || columnWidth.IsAuto;
|
||||
if (column != lastVisibleColumn)
|
||||
{
|
||||
cell.EnsureGridLine(lastVisibleColumn);
|
||||
}
|
||||
|
||||
// If we're not using star sizing or the current column can't be resized,
|
||||
// then just set the display width according to the column's desired width
|
||||
if (!this.OwningGrid.UsesStarSizing || (!column.ActualCanUserResize && !column.Width.IsStar))
|
||||
{
|
||||
// In the edge-case where we're given infinite width and we have star columns, the
|
||||
// star columns grow to their predefined limit of 10,000 (or their MaxWidth)
|
||||
double newDisplayWidth = column.Width.IsStar ?
|
||||
Math.Min(column.ActualMaxWidth, DataGrid.DATAGRID_maximumStarColumnWidth) :
|
||||
Math.Max(column.ActualMinWidth, Math.Min(column.ActualMaxWidth, column.Width.DesiredValue));
|
||||
column.SetWidthDisplayValue(newDisplayWidth);
|
||||
}
|
||||
|
||||
// If we're auto-growing the column based on the cell content, we want to measure it at its maximum value
|
||||
if (autoGrowWidth)
|
||||
{
|
||||
cell.Measure(new Size(column.ActualMaxWidth, measureHeight));
|
||||
this.OwningGrid.AutoSizeColumn(column, cell.DesiredSize.Width);
|
||||
column.ComputeLayoutRoundedWidth(totalDisplayWidth);
|
||||
}
|
||||
else if (!this.OwningGrid.UsesStarSizing)
|
||||
{
|
||||
column.ComputeLayoutRoundedWidth(scrollingLeftEdge);
|
||||
cell.Measure(new Size(column.LayoutRoundedWidth, measureHeight));
|
||||
}
|
||||
|
||||
// We need to track the largest height in order to auto-size
|
||||
if (autoSizeHeight)
|
||||
{
|
||||
this.DesiredHeight = Math.Max(this.DesiredHeight, cell.DesiredSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
if (column.IsFrozen)
|
||||
{
|
||||
frozenLeftEdge += column.ActualWidth;
|
||||
}
|
||||
|
||||
scrollingLeftEdge += column.ActualWidth;
|
||||
totalDisplayWidth += column.ActualWidth;
|
||||
}
|
||||
|
||||
// If we're using star sizing (and we're not waiting for an auto-column to finish growing)
|
||||
// then we will resize all the columns to fit the available space.
|
||||
if (this.OwningGrid.UsesStarSizing && !this.OwningGrid.AutoSizingColumns)
|
||||
{
|
||||
double adjustment = this.OwningGrid.CellsWidth - totalDisplayWidth;
|
||||
this.OwningGrid.AdjustColumnWidths(0, adjustment, false);
|
||||
|
||||
// Since we didn't know the final widths of the columns until we resized,
|
||||
// we waited until now to measure each cell
|
||||
double leftEdge = 0;
|
||||
foreach (DataGridColumn column in this.OwningGrid.ColumnsInternal.GetVisibleColumns())
|
||||
{
|
||||
DataGridCell cell = this.OwningRow.Cells[column.Index];
|
||||
column.ComputeLayoutRoundedWidth(leftEdge);
|
||||
cell.Measure(new Size(column.LayoutRoundedWidth, measureHeight));
|
||||
if (autoSizeHeight)
|
||||
{
|
||||
this.DesiredHeight = Math.Max(this.DesiredHeight, cell.DesiredSize.Height);
|
||||
}
|
||||
|
||||
leftEdge += column.ActualWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Measure FillerCell, we're doing it unconditionally here because we don't know if we'll need the filler
|
||||
// column and we don't want to cause another Measure if we do
|
||||
this.OwningRow.FillerCell.Measure(new Size(double.PositiveInfinity, this.DesiredHeight));
|
||||
|
||||
this.OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
|
||||
return new Size(this.OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, this.DesiredHeight);
|
||||
}
|
||||
|
||||
internal void Recycle()
|
||||
{
|
||||
// Clear out the cached desired height so it is not reused for other rows
|
||||
this.DesiredHeight = 0;
|
||||
|
||||
if (this.OwningGrid != null && this.OwningRow != null)
|
||||
{
|
||||
foreach (DataGridColumn column in this.OwningGrid.ColumnsInternal)
|
||||
{
|
||||
this.OwningRow.Cells[column.Index].Recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge)
|
||||
{
|
||||
Debug.Assert(this.OwningGrid != null, "Expected non-null owning DataGrid.");
|
||||
Debug.Assert(this.OwningGrid.HorizontalAdjustment >= 0, "Expected owning positive DataGrid.HorizontalAdjustment.");
|
||||
Debug.Assert(this.OwningGrid.HorizontalAdjustment <= this.OwningGrid.HorizontalOffset, "Expected owning DataGrid.HorizontalAdjustment smaller than or equal to DataGrid.HorizontalOffset.");
|
||||
|
||||
if (column.Visibility != Visibility.Visible)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
scrollingLeftEdge += this.OwningGrid.HorizontalAdjustment;
|
||||
double leftEdge = column.IsFrozen ? frozenLeftEdge : scrollingLeftEdge;
|
||||
double rightEdge = leftEdge + column.ActualWidth;
|
||||
return DoubleUtil.GreaterThan(rightEdge, 0) &&
|
||||
DoubleUtil.LessThanOrClose(leftEdge, this.OwningGrid.CellsWidth) &&
|
||||
DoubleUtil.GreaterThan(rightEdge, frozenLeftEdge); // scrolling column covered up by frozen column(s)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,361 @@
|
|||
// 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.Specialized;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="DataGrid"/> column that hosts
|
||||
/// <see cref="T:System.Windows.Controls.CheckBox"/> controls in its cells.
|
||||
/// </summary>
|
||||
[StyleTypedProperty(Property = "ElementStyle", StyleTargetType = typeof(CheckBox))]
|
||||
[StyleTypedProperty(Property = "EditingElementStyle", StyleTargetType = typeof(CheckBox))]
|
||||
public class DataGridCheckBoxColumn : DataGridBoundColumn
|
||||
{
|
||||
private const string DATAGRIDCHECKBOXCOLUMN_isThreeStateName = "IsThreeState";
|
||||
private const double DATAGRIDCHECKBOXCOLUMN_leftMargin = 12.0;
|
||||
|
||||
private bool _beganEditWithKeyboard;
|
||||
private bool _isThreeState;
|
||||
private CheckBox _currentCheckBox;
|
||||
private DataGrid _owningGrid;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridCheckBoxColumn"/> class.
|
||||
/// </summary>
|
||||
public DataGridCheckBoxColumn()
|
||||
{
|
||||
this.BindingTarget = CheckBox.IsCheckedProperty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the hosted <see cref="T:System.Windows.Controls.CheckBox"/> controls allow three states or two.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if the hosted controls support three states; false if they support two states. The default is false.
|
||||
/// </returns>
|
||||
public bool IsThreeState
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isThreeState;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_isThreeState != value)
|
||||
{
|
||||
_isThreeState = value;
|
||||
NotifyPropertyChanged(DATAGRIDCHECKBOXCOLUMN_isThreeStateName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Causes the column cell being edited to revert to the specified value.
|
||||
/// </summary>
|
||||
/// <param name="editingElement">
|
||||
/// The element that the column displays for a cell in editing mode.
|
||||
/// </param>
|
||||
/// <param name="uneditedValue">
|
||||
/// The previous, unedited value in the cell being edited.
|
||||
/// </param>
|
||||
protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
|
||||
{
|
||||
CheckBox editingCheckBox = editingElement as CheckBox;
|
||||
if (editingCheckBox != null)
|
||||
{
|
||||
editingCheckBox.IsChecked = (bool?)uneditedValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="T:System.Windows.Controls.CheckBox"/> control that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.
|
||||
/// </summary>
|
||||
/// <param name="cell">
|
||||
/// The cell that will contain the generated element.
|
||||
/// </param>
|
||||
/// <param name="dataItem">
|
||||
/// The data item represented by the row that contains the intended cell.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A new <see cref="T:System.Windows.Controls.CheckBox"/> control that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.
|
||||
/// </returns>
|
||||
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
CheckBox checkBox = new CheckBox();
|
||||
ConfigureCheckBox(checkBox, (cell != null & cell.OwningRow != null) ? cell.OwningRow.ComputedForeground : null);
|
||||
return checkBox;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only <see cref="T:System.Windows.Controls.CheckBox"/> control that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.
|
||||
/// </summary>
|
||||
/// <param name="cell">
|
||||
/// The cell that will contain the generated element.
|
||||
/// </param>
|
||||
/// <param name="dataItem">
|
||||
/// The data item represented by the row that contains the intended cell.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A new, read-only <see cref="T:System.Windows.Controls.CheckBox"/> control that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.
|
||||
/// </returns>
|
||||
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
bool isEnabled = false;
|
||||
CheckBox checkBoxElement = new CheckBox();
|
||||
if (EnsureOwningGrid())
|
||||
{
|
||||
if (cell.RowIndex != -1 && cell.ColumnIndex != -1 &&
|
||||
cell.OwningRow != null &&
|
||||
cell.OwningRow.Slot == this.OwningGrid.CurrentSlot &&
|
||||
cell.ColumnIndex == this.OwningGrid.CurrentColumnIndex)
|
||||
{
|
||||
isEnabled = true;
|
||||
if (_currentCheckBox != null)
|
||||
{
|
||||
_currentCheckBox.IsEnabled = false;
|
||||
}
|
||||
|
||||
_currentCheckBox = checkBoxElement;
|
||||
}
|
||||
}
|
||||
|
||||
checkBoxElement.IsEnabled = isEnabled;
|
||||
checkBoxElement.IsHitTestVisible = false;
|
||||
checkBoxElement.IsTabStop = false;
|
||||
ConfigureCheckBox(checkBoxElement, (cell != null & cell.OwningRow != null) ? cell.OwningRow.ComputedForeground : null);
|
||||
return checkBoxElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a cell in the column enters editing mode.
|
||||
/// </summary>
|
||||
/// <param name="editingElement">
|
||||
/// The element that the column displays for a cell in editing mode.
|
||||
/// </param>
|
||||
/// <param name="editingEventArgs">
|
||||
/// Information about the user gesture that is causing a cell to enter editing mode.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The unedited value.
|
||||
/// </returns>
|
||||
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
|
||||
{
|
||||
CheckBox editingCheckBox = editingElement as CheckBox;
|
||||
if (editingCheckBox != null)
|
||||
{
|
||||
bool? uneditedValue = editingCheckBox.IsChecked;
|
||||
|
||||
PointerRoutedEventArgs pointerEventArgs = editingEventArgs as PointerRoutedEventArgs;
|
||||
bool editValue = false;
|
||||
if (pointerEventArgs != null)
|
||||
{
|
||||
// Editing was triggered by a mouse click
|
||||
Point position = pointerEventArgs.GetCurrentPoint(editingCheckBox).Position;
|
||||
Rect rect = new Rect(0, 0, editingCheckBox.ActualWidth, editingCheckBox.ActualHeight);
|
||||
editValue = rect.Contains(position);
|
||||
}
|
||||
else if (_beganEditWithKeyboard)
|
||||
{
|
||||
// Editing began by a user pressing spacebar
|
||||
editValue = true;
|
||||
_beganEditWithKeyboard = false;
|
||||
}
|
||||
|
||||
if (editValue)
|
||||
{
|
||||
// User clicked the checkbox itself or pressed space, let's toggle the IsChecked value
|
||||
if (editingCheckBox.IsThreeState)
|
||||
{
|
||||
switch (editingCheckBox.IsChecked)
|
||||
{
|
||||
case false:
|
||||
editingCheckBox.IsChecked = true;
|
||||
break;
|
||||
case true:
|
||||
editingCheckBox.IsChecked = null;
|
||||
break;
|
||||
case null:
|
||||
editingCheckBox.IsChecked = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
editingCheckBox.IsChecked = !editingCheckBox.IsChecked;
|
||||
}
|
||||
}
|
||||
|
||||
return uneditedValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the DataGrid control when this column asks for its elements to be
|
||||
/// updated, because its CheckBoxContent or IsThreeState property changed.
|
||||
/// </summary>
|
||||
protected internal override void RefreshCellContent(FrameworkElement element, Brush computedRowForeground, string propertyName)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException("element");
|
||||
}
|
||||
|
||||
CheckBox checkBox = element as CheckBox;
|
||||
if (checkBox == null)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueIsNotAnInstanceOf("element", typeof(CheckBox));
|
||||
}
|
||||
|
||||
if (propertyName == DATAGRIDCHECKBOXCOLUMN_isThreeStateName)
|
||||
{
|
||||
checkBox.IsThreeState = this.IsThreeState;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkBox.IsThreeState = this.IsThreeState;
|
||||
checkBox.Foreground = computedRowForeground;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the computed foreground of a row changed.
|
||||
/// </summary>
|
||||
protected internal override void RefreshForeground(FrameworkElement element, Brush computedRowForeground)
|
||||
{
|
||||
CheckBox checkBox = element as CheckBox;
|
||||
if (checkBox != null)
|
||||
{
|
||||
checkBox.Foreground = computedRowForeground;
|
||||
}
|
||||
}
|
||||
|
||||
private void Columns_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (this.OwningGrid == null && _owningGrid != null)
|
||||
{
|
||||
_owningGrid.Columns.CollectionChanged -= new NotifyCollectionChangedEventHandler(Columns_CollectionChanged);
|
||||
_owningGrid.CurrentCellChanged -= new EventHandler<EventArgs>(OwningGrid_CurrentCellChanged);
|
||||
_owningGrid.KeyDown -= new KeyEventHandler(OwningGrid_KeyDown);
|
||||
_owningGrid.LoadingRow -= new EventHandler<DataGridRowEventArgs>(OwningGrid_LoadingRow);
|
||||
_owningGrid = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureCheckBox(CheckBox checkBox, Brush computedRowForeground)
|
||||
{
|
||||
checkBox.Margin = new Thickness(DATAGRIDCHECKBOXCOLUMN_leftMargin, 0.0, 0.0, 0.0);
|
||||
checkBox.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
checkBox.VerticalAlignment = VerticalAlignment.Center;
|
||||
checkBox.IsThreeState = this.IsThreeState;
|
||||
checkBox.UseSystemFocusVisuals = false;
|
||||
checkBox.Foreground = computedRowForeground;
|
||||
if (this.Binding != null)
|
||||
{
|
||||
checkBox.SetBinding(this.BindingTarget, this.Binding);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnsureOwningGrid()
|
||||
{
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
if (this.OwningGrid != _owningGrid)
|
||||
{
|
||||
_owningGrid = this.OwningGrid;
|
||||
_owningGrid.Columns.CollectionChanged += new NotifyCollectionChangedEventHandler(Columns_CollectionChanged);
|
||||
_owningGrid.CurrentCellChanged += new EventHandler<EventArgs>(OwningGrid_CurrentCellChanged);
|
||||
_owningGrid.KeyDown += new KeyEventHandler(OwningGrid_KeyDown);
|
||||
_owningGrid.LoadingRow += new EventHandler<DataGridRowEventArgs>(OwningGrid_LoadingRow);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OwningGrid_CurrentCellChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_currentCheckBox != null)
|
||||
{
|
||||
_currentCheckBox.IsEnabled = false;
|
||||
}
|
||||
|
||||
if (this.OwningGrid != null && this.OwningGrid.CurrentColumn == this &&
|
||||
this.OwningGrid.IsSlotVisible(this.OwningGrid.CurrentSlot))
|
||||
{
|
||||
DataGridRow row = this.OwningGrid.DisplayData.GetDisplayedElement(this.OwningGrid.CurrentSlot) as DataGridRow;
|
||||
if (row != null)
|
||||
{
|
||||
CheckBox checkBox = this.GetCellContent(row) as CheckBox;
|
||||
if (checkBox != null)
|
||||
{
|
||||
checkBox.IsEnabled = true;
|
||||
}
|
||||
|
||||
_currentCheckBox = checkBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OwningGrid_KeyDown(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
if (e.Key == Windows.System.VirtualKey.Space &&
|
||||
this.OwningGrid != null &&
|
||||
this.OwningGrid.CurrentColumn == this)
|
||||
{
|
||||
DataGridRow row = this.OwningGrid.DisplayData.GetDisplayedElement(this.OwningGrid.CurrentSlot) as DataGridRow;
|
||||
if (row != null)
|
||||
{
|
||||
CheckBox checkBox = this.GetCellContent(row) as CheckBox;
|
||||
if (checkBox == _currentCheckBox)
|
||||
{
|
||||
_beganEditWithKeyboard = true;
|
||||
this.OwningGrid.BeginEdit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_beganEditWithKeyboard = false;
|
||||
}
|
||||
|
||||
private void OwningGrid_LoadingRow(object sender, DataGridRowEventArgs e)
|
||||
{
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
CheckBox checkBox = this.GetCellContent(e.Row) as CheckBox;
|
||||
if (checkBox != null)
|
||||
{
|
||||
if (this.OwningGrid.CurrentColumnIndex == this.Index && this.OwningGrid.CurrentSlot == e.Row.Slot)
|
||||
{
|
||||
if (_currentCheckBox != null)
|
||||
{
|
||||
_currentCheckBox.IsEnabled = false;
|
||||
}
|
||||
|
||||
checkBox.IsEnabled = true;
|
||||
_currentCheckBox = checkBox;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkBox.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
// 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.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// This structure encapsulate the cell information necessary when clipboard content is prepared.
|
||||
/// </summary>
|
||||
public struct DataGridClipboardCellContent
|
||||
{
|
||||
private DataGridColumn _column;
|
||||
private object _content;
|
||||
private object _item;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridClipboardCellContent"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="item">DataGrid row item containing the cell.</param>
|
||||
/// <param name="column">DataGridColumn containing the cell.</param>
|
||||
/// <param name="content">DataGrid cell value.</param>
|
||||
public DataGridClipboardCellContent(object item, DataGridColumn column, object content)
|
||||
{
|
||||
_item = item;
|
||||
_column = column;
|
||||
_content = content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="DataGridColumn"/> column containing the cell.
|
||||
/// </summary>
|
||||
public DataGridColumn Column
|
||||
{
|
||||
get
|
||||
{
|
||||
return _column;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="DataGridCell"/> cell content.
|
||||
/// </summary>
|
||||
public object Content
|
||||
{
|
||||
get
|
||||
{
|
||||
return _content;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="DataGridRow"/> row item containing the cell.
|
||||
/// </summary>
|
||||
public object Item
|
||||
{
|
||||
get
|
||||
{
|
||||
return _item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Field-by-field comparison to avoid reflection-based ValueType.Equals.
|
||||
/// </summary>
|
||||
/// <param name="obj">DataGridClipboardCellContent to compare.</param>
|
||||
/// <returns>True if this and data are equal</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is DataGridClipboardCellContent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DataGridClipboardCellContent clipboardCellContent = (DataGridClipboardCellContent)obj;
|
||||
return _column == clipboardCellContent._column && _content == clipboardCellContent._content && _item == clipboardCellContent._item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a deterministic hash code.
|
||||
/// </summary>
|
||||
/// <returns>Hash value.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (_column.GetHashCode() ^ _content.GetHashCode()) ^ _item.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Field-by-field comparison to avoid reflection-based ValueType.Equals.
|
||||
/// </summary>
|
||||
/// <param name="clipboardCellContent1">The first DataGridClipboardCellContent.</param>
|
||||
/// <param name="clipboardCellContent2">The second DataGridClipboardCellContent.</param>
|
||||
/// <returns>True if and only if clipboardCellContent1 and clipboardCellContent2 are equal.</returns>
|
||||
public static bool operator ==(DataGridClipboardCellContent clipboardCellContent1, DataGridClipboardCellContent clipboardCellContent2)
|
||||
{
|
||||
return clipboardCellContent1._column == clipboardCellContent2._column && clipboardCellContent1._content == clipboardCellContent2._content && clipboardCellContent1._item == clipboardCellContent2._item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Field-by-field comparison to avoid reflection-based ValueType.Equals.
|
||||
/// </summary>
|
||||
/// <param name="clipboardCellContent1">The first DataGridClipboardCellContent.</param>
|
||||
/// <param name="clipboardCellContent2">The second DataGridClipboardCellContent.</param>
|
||||
/// <returns>True if clipboardCellContent1 and clipboardCellContent2 are NOT equal.</returns>
|
||||
public static bool operator !=(DataGridClipboardCellContent clipboardCellContent1, DataGridClipboardCellContent clipboardCellContent2)
|
||||
{
|
||||
if (clipboardCellContent1._column == clipboardCellContent2._column && clipboardCellContent1._content == clipboardCellContent2._content)
|
||||
{
|
||||
return clipboardCellContent1._item != clipboardCellContent2._item;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,659 @@
|
|||
// 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.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
internal class DataGridColumnCollection : ObservableCollection<DataGridColumn>
|
||||
{
|
||||
private DataGrid _owningGrid;
|
||||
|
||||
public DataGridColumnCollection(DataGrid owningGrid)
|
||||
{
|
||||
_owningGrid = owningGrid;
|
||||
this.ItemsInternal = new List<DataGridColumn>();
|
||||
this.FillerColumn = new DataGridFillerColumn(owningGrid);
|
||||
this.DisplayIndexMap = new List<int>();
|
||||
this.RowGroupSpacerColumn = new DataGridFillerColumn(owningGrid);
|
||||
}
|
||||
|
||||
internal int AutogeneratedColumnCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal List<int> DisplayIndexMap
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal DataGridFillerColumn FillerColumn
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal DataGridColumn FirstColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetFirstColumn(null /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridColumn FirstVisibleColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetFirstColumn(true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridColumn FirstVisibleNonFillerColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
DataGridColumn dataGridColumn = this.FirstVisibleColumn;
|
||||
if (dataGridColumn == this.RowGroupSpacerColumn)
|
||||
{
|
||||
dataGridColumn = GetNextVisibleColumn(dataGridColumn);
|
||||
}
|
||||
|
||||
return dataGridColumn;
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridColumn FirstVisibleWritableColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetFirstColumn(true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/);
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridColumn FirstVisibleScrollingColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetFirstColumn(true /*isVisible*/, false /*isFrozen*/, null /*isReadOnly*/);
|
||||
}
|
||||
}
|
||||
|
||||
internal List<DataGridColumn> ItemsInternal
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal DataGridColumn LastVisibleColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetLastColumn(true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridColumn LastVisibleScrollingColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetLastColumn(true /*isVisible*/, false /*isFrozen*/, null /*isReadOnly*/);
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridColumn LastVisibleWritableColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetLastColumn(true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/);
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridFillerColumn RowGroupSpacerColumn
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal int VisibleColumnCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int visibleColumnCount = 0;
|
||||
for (int columnIndex = 0; columnIndex < this.ItemsInternal.Count; columnIndex++)
|
||||
{
|
||||
if (this.ItemsInternal[columnIndex].IsVisible)
|
||||
{
|
||||
visibleColumnCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return visibleColumnCount;
|
||||
}
|
||||
}
|
||||
|
||||
internal double VisibleEdgedColumnsWidth
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of star columns that are currently visible.
|
||||
/// NOTE: Requires that EnsureVisibleEdgedColumnsWidth has been called.
|
||||
/// </summary>
|
||||
internal int VisibleStarColumnCount
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
Debug.Assert(_owningGrid != null, "Expected non-null owning DataGrid.");
|
||||
try
|
||||
{
|
||||
_owningGrid.NoCurrentCellChangeCount++;
|
||||
if (this.ItemsInternal.Count > 0)
|
||||
{
|
||||
if (_owningGrid.InDisplayIndexAdjustments)
|
||||
{
|
||||
// We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes.
|
||||
throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes();
|
||||
}
|
||||
|
||||
_owningGrid.OnClearingColumns();
|
||||
for (int columnIndex = 0; columnIndex < this.ItemsInternal.Count; columnIndex++)
|
||||
{
|
||||
// Detach the column...
|
||||
this.ItemsInternal[columnIndex].OwningGrid = null;
|
||||
}
|
||||
|
||||
this.ItemsInternal.Clear();
|
||||
this.DisplayIndexMap.Clear();
|
||||
this.AutogeneratedColumnCount = 0;
|
||||
_owningGrid.OnColumnCollectionChanged_PreNotification(false /*columnsGrew*/);
|
||||
base.ClearItems();
|
||||
this.VisibleEdgedColumnsWidth = 0;
|
||||
_owningGrid.OnColumnCollectionChanged_PostNotification(false /*columnsGrew*/);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_owningGrid.NoCurrentCellChangeCount--;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void InsertItem(int columnIndex, DataGridColumn dataGridColumn)
|
||||
{
|
||||
Debug.Assert(_owningGrid != null, "Expected non-null owning DataGrid.");
|
||||
try
|
||||
{
|
||||
_owningGrid.NoCurrentCellChangeCount++;
|
||||
if (_owningGrid.InDisplayIndexAdjustments)
|
||||
{
|
||||
// We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes.
|
||||
throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes();
|
||||
}
|
||||
|
||||
if (dataGridColumn == null)
|
||||
{
|
||||
throw new ArgumentNullException("dataGridColumn");
|
||||
}
|
||||
|
||||
int columnIndexWithFiller = columnIndex;
|
||||
if (dataGridColumn != this.RowGroupSpacerColumn && this.RowGroupSpacerColumn.IsRepresented)
|
||||
{
|
||||
columnIndexWithFiller++;
|
||||
}
|
||||
|
||||
// get the new current cell coordinates
|
||||
DataGridCellCoordinates newCurrentCellCoordinates = _owningGrid.OnInsertingColumn(columnIndex, dataGridColumn);
|
||||
|
||||
// insert the column into our internal list
|
||||
this.ItemsInternal.Insert(columnIndexWithFiller, dataGridColumn);
|
||||
dataGridColumn.Index = columnIndexWithFiller;
|
||||
dataGridColumn.OwningGrid = _owningGrid;
|
||||
dataGridColumn.RemoveEditingElement();
|
||||
if (dataGridColumn.IsVisible)
|
||||
{
|
||||
this.VisibleEdgedColumnsWidth += dataGridColumn.ActualWidth;
|
||||
}
|
||||
|
||||
// continue with the base insert
|
||||
_owningGrid.OnInsertedColumn_PreNotification(dataGridColumn);
|
||||
_owningGrid.OnColumnCollectionChanged_PreNotification(true /*columnsGrew*/);
|
||||
|
||||
if (dataGridColumn != this.RowGroupSpacerColumn)
|
||||
{
|
||||
base.InsertItem(columnIndex, dataGridColumn);
|
||||
}
|
||||
|
||||
_owningGrid.OnInsertedColumn_PostNotification(newCurrentCellCoordinates, dataGridColumn.DisplayIndex);
|
||||
_owningGrid.OnColumnCollectionChanged_PostNotification(true /*columnsGrew*/);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_owningGrid.NoCurrentCellChangeCount--;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int columnIndex)
|
||||
{
|
||||
RemoveItemPrivate(columnIndex, false /*isSpacer*/);
|
||||
}
|
||||
|
||||
protected override void SetItem(int columnIndex, DataGridColumn dataGridColumn)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
internal bool DisplayInOrder(int columnIndex1, int columnIndex2)
|
||||
{
|
||||
int displayIndex1 = ((DataGridColumn)this.ItemsInternal[columnIndex1]).DisplayIndexWithFiller;
|
||||
int displayIndex2 = ((DataGridColumn)this.ItemsInternal[columnIndex2]).DisplayIndexWithFiller;
|
||||
return displayIndex1 < displayIndex2;
|
||||
}
|
||||
|
||||
internal bool EnsureRowGrouping(bool rowGrouping)
|
||||
{
|
||||
// The insert below could cause the first column to be added. That causes a refresh
|
||||
// which re-enters this method so instead of checking RowGroupSpacerColumn.IsRepresented,
|
||||
// we need to check to see if it's actually in our collection instead.
|
||||
bool spacerRepresented = (this.ItemsInternal.Count > 0) && (this.ItemsInternal[0] == this.RowGroupSpacerColumn);
|
||||
if (rowGrouping && !spacerRepresented)
|
||||
{
|
||||
this.Insert(0, this.RowGroupSpacerColumn);
|
||||
this.RowGroupSpacerColumn.IsRepresented = true;
|
||||
return true;
|
||||
}
|
||||
else if (!rowGrouping && spacerRepresented)
|
||||
{
|
||||
Debug.Assert(this.ItemsInternal[0] == this.RowGroupSpacerColumn, "Unexpected RowGroupSpacerColumn value.");
|
||||
|
||||
// We need to set IsRepresented to false before removing the RowGroupSpacerColumn
|
||||
// otherwise, we'll remove the column after it
|
||||
this.RowGroupSpacerColumn.IsRepresented = false;
|
||||
RemoveItemPrivate(0, true /*isSpacer*/);
|
||||
Debug.Assert(this.DisplayIndexMap.Count == this.ItemsInternal.Count, "Unexpected DisplayIndexMap.Count value.");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In addition to ensuring that column widths are valid, this method updates the values of
|
||||
/// VisibleEdgedColumnsWidth and VisibleStarColumnCount.
|
||||
/// </summary>
|
||||
internal void EnsureVisibleEdgedColumnsWidth()
|
||||
{
|
||||
this.VisibleStarColumnCount = 0;
|
||||
this.VisibleEdgedColumnsWidth = 0;
|
||||
for (int columnIndex = 0; columnIndex < this.ItemsInternal.Count; columnIndex++)
|
||||
{
|
||||
if (this.ItemsInternal[columnIndex].IsVisible)
|
||||
{
|
||||
this.ItemsInternal[columnIndex].EnsureWidth();
|
||||
if (this.ItemsInternal[columnIndex].Width.IsStar)
|
||||
{
|
||||
this.VisibleStarColumnCount++;
|
||||
}
|
||||
|
||||
this.VisibleEdgedColumnsWidth += this.ItemsInternal[columnIndex].ActualWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridColumn GetColumnAtDisplayIndex(int displayIndex)
|
||||
{
|
||||
if (displayIndex < 0 || displayIndex >= this.ItemsInternal.Count || displayIndex >= this.DisplayIndexMap.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int columnIndex = this.DisplayIndexMap[displayIndex];
|
||||
return this.ItemsInternal[columnIndex];
|
||||
}
|
||||
|
||||
internal int GetColumnCount(bool isVisible, bool isFrozen, int fromColumnIndex, int toColumnIndex)
|
||||
{
|
||||
Debug.Assert(DisplayInOrder(fromColumnIndex, toColumnIndex), "Unexpected column display order.");
|
||||
Debug.Assert(this.ItemsInternal[toColumnIndex].IsVisible == isVisible, "Unexpected column visibility state.");
|
||||
Debug.Assert(this.ItemsInternal[toColumnIndex].IsFrozen == isFrozen, "Unexpected column frozen state.");
|
||||
|
||||
int columnCount = 0;
|
||||
DataGridColumn dataGridColumn = this.ItemsInternal[fromColumnIndex];
|
||||
|
||||
while (dataGridColumn != this.ItemsInternal[toColumnIndex])
|
||||
{
|
||||
dataGridColumn = GetNextColumn(dataGridColumn, isVisible, isFrozen, null /*isReadOnly*/);
|
||||
Debug.Assert(dataGridColumn != null, "Expected non-null dataGridColumn.");
|
||||
columnCount++;
|
||||
}
|
||||
|
||||
return columnCount;
|
||||
}
|
||||
|
||||
internal IEnumerable<DataGridColumn> GetDisplayedColumns()
|
||||
{
|
||||
Debug.Assert(this.ItemsInternal.Count == this.DisplayIndexMap.Count, "Unexpected DisplayIndexMap.Count value.");
|
||||
foreach (int columnIndex in this.DisplayIndexMap)
|
||||
{
|
||||
yield return this.ItemsInternal[columnIndex];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumeration of all columns that meet the criteria of the filter predicate.
|
||||
/// </summary>
|
||||
/// <param name="filter">Criteria for inclusion.</param>
|
||||
/// <returns>Columns that meet the criteria, in ascending DisplayIndex order.</returns>
|
||||
internal IEnumerable<DataGridColumn> GetDisplayedColumns(Predicate<DataGridColumn> filter)
|
||||
{
|
||||
Debug.Assert(filter != null, "Expected non-null filter.");
|
||||
Debug.Assert(this.ItemsInternal.Count == this.DisplayIndexMap.Count, "Unexpected DisplayIndexMap.Count value.");
|
||||
foreach (int columnIndex in this.DisplayIndexMap)
|
||||
{
|
||||
DataGridColumn column = this.ItemsInternal[columnIndex];
|
||||
if (filter(column))
|
||||
{
|
||||
yield return column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumeration of all columns that meet the criteria of the filter predicate.
|
||||
/// The columns are returned in the order specified by the reverse flag.
|
||||
/// </summary>
|
||||
/// <param name="reverse">Whether or not to return the columns in descending DisplayIndex order.</param>
|
||||
/// <param name="filter">Criteria for inclusion.</param>
|
||||
/// <returns>Columns that meet the criteria, in the order specified by the reverse flag.</returns>
|
||||
internal IEnumerable<DataGridColumn> GetDisplayedColumns(bool reverse, Predicate<DataGridColumn> filter)
|
||||
{
|
||||
return reverse ? GetDisplayedColumnsReverse(filter) : GetDisplayedColumns(filter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumeration of all columns that meet the criteria of the filter predicate.
|
||||
/// The columns are returned in descending DisplayIndex order.
|
||||
/// </summary>
|
||||
/// <param name="filter">Criteria for inclusion.</param>
|
||||
/// <returns>Columns that meet the criteria, in descending DisplayIndex order.</returns>
|
||||
internal IEnumerable<DataGridColumn> GetDisplayedColumnsReverse(Predicate<DataGridColumn> filter)
|
||||
{
|
||||
Debug.Assert(filter != null, "Expected non-null filter.");
|
||||
Debug.Assert(this.ItemsInternal.Count == this.DisplayIndexMap.Count, "Unexpected DisplayIndexMap.Count value.");
|
||||
for (int displayIndex = this.DisplayIndexMap.Count - 1; displayIndex >= 0; displayIndex--)
|
||||
{
|
||||
DataGridColumn column = this.ItemsInternal[this.DisplayIndexMap[displayIndex]];
|
||||
if (filter(column))
|
||||
{
|
||||
yield return column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridColumn GetFirstColumn(bool? isVisible, bool? isFrozen, bool? isReadOnly)
|
||||
{
|
||||
Debug.Assert(this.ItemsInternal.Count == this.DisplayIndexMap.Count, "Unexpected DisplayIndexMap.Count value.");
|
||||
int index = 0;
|
||||
while (index < this.DisplayIndexMap.Count)
|
||||
{
|
||||
DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index);
|
||||
if ((isVisible == null || dataGridColumn.IsVisible == isVisible) &&
|
||||
(isFrozen == null || dataGridColumn.IsFrozen == isFrozen) &&
|
||||
(isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly))
|
||||
{
|
||||
return dataGridColumn;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal DataGridColumn GetLastColumn(bool? isVisible, bool? isFrozen, bool? isReadOnly)
|
||||
{
|
||||
Debug.Assert(this.ItemsInternal.Count == this.DisplayIndexMap.Count, "Unexpected DisplayIndexMap.Count value.");
|
||||
int index = this.DisplayIndexMap.Count - 1;
|
||||
while (index >= 0)
|
||||
{
|
||||
DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index);
|
||||
if ((isVisible == null || dataGridColumn.IsVisible == isVisible) &&
|
||||
(isFrozen == null || dataGridColumn.IsFrozen == isFrozen) &&
|
||||
(isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly))
|
||||
{
|
||||
return dataGridColumn;
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal DataGridColumn GetNextColumn(DataGridColumn dataGridColumnStart)
|
||||
{
|
||||
return GetNextColumn(dataGridColumnStart, null /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
|
||||
}
|
||||
|
||||
internal DataGridColumn GetNextColumn(
|
||||
DataGridColumn dataGridColumnStart,
|
||||
bool? isVisible,
|
||||
bool? isFrozen,
|
||||
bool? isReadOnly)
|
||||
{
|
||||
Debug.Assert(dataGridColumnStart != null, "Expected non-null dataGridColumnStart.");
|
||||
Debug.Assert(this.ItemsInternal.Contains(dataGridColumnStart), "Expected dataGridColumnStart in ItemsInternal.");
|
||||
Debug.Assert(this.ItemsInternal.Count == this.DisplayIndexMap.Count, "Unexpected DisplayIndexMap.Count value.");
|
||||
|
||||
int index = dataGridColumnStart.DisplayIndexWithFiller + 1;
|
||||
while (index < this.DisplayIndexMap.Count)
|
||||
{
|
||||
DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index);
|
||||
|
||||
if ((isVisible == null || dataGridColumn.IsVisible == isVisible) &&
|
||||
(isFrozen == null || dataGridColumn.IsFrozen == isFrozen) &&
|
||||
(isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly))
|
||||
{
|
||||
return dataGridColumn;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal DataGridColumn GetNextVisibleColumn(DataGridColumn dataGridColumnStart)
|
||||
{
|
||||
return GetNextColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
|
||||
}
|
||||
|
||||
internal DataGridColumn GetNextVisibleFrozenColumn(DataGridColumn dataGridColumnStart)
|
||||
{
|
||||
return GetNextColumn(dataGridColumnStart, true /*isVisible*/, true /*isFrozen*/, null /*isReadOnly*/);
|
||||
}
|
||||
|
||||
internal DataGridColumn GetNextVisibleWritableColumn(DataGridColumn dataGridColumnStart)
|
||||
{
|
||||
return GetNextColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/);
|
||||
}
|
||||
|
||||
internal DataGridColumn GetPreviousColumn(
|
||||
DataGridColumn dataGridColumnStart,
|
||||
bool? isVisible,
|
||||
bool? isFrozen,
|
||||
bool? isReadOnly)
|
||||
{
|
||||
Debug.Assert(dataGridColumnStart != null, "Expected non-null dataGridColumnStart.");
|
||||
Debug.Assert(this.ItemsInternal.Contains(dataGridColumnStart), "Expected dataGridColumnStart in ItemsInternal.");
|
||||
Debug.Assert(this.ItemsInternal.Count == this.DisplayIndexMap.Count, "Unexpected DisplayIndexMap.Count value.");
|
||||
|
||||
int index = dataGridColumnStart.DisplayIndexWithFiller - 1;
|
||||
while (index >= 0)
|
||||
{
|
||||
DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index);
|
||||
if ((isVisible == null || dataGridColumn.IsVisible == isVisible) &&
|
||||
(isFrozen == null || dataGridColumn.IsFrozen == isFrozen) &&
|
||||
(isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly))
|
||||
{
|
||||
return dataGridColumn;
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal DataGridColumn GetPreviousVisibleNonFillerColumn(DataGridColumn dataGridColumnStart)
|
||||
{
|
||||
DataGridColumn column = GetPreviousColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
|
||||
return (column is DataGridFillerColumn) ? null : column;
|
||||
}
|
||||
|
||||
internal DataGridColumn GetPreviousVisibleScrollingColumn(DataGridColumn dataGridColumnStart)
|
||||
{
|
||||
return GetPreviousColumn(dataGridColumnStart, true /*isVisible*/, false /*isFrozen*/, null /*isReadOnly*/);
|
||||
}
|
||||
|
||||
internal DataGridColumn GetPreviousVisibleWritableColumn(DataGridColumn dataGridColumnStart)
|
||||
{
|
||||
return GetPreviousColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/);
|
||||
}
|
||||
|
||||
internal int GetVisibleColumnCount(int fromColumnIndex, int toColumnIndex)
|
||||
{
|
||||
Debug.Assert(DisplayInOrder(fromColumnIndex, toColumnIndex), "Unexpected column display order.");
|
||||
Debug.Assert(this.ItemsInternal[toColumnIndex].IsVisible, "Unexpected column visibility state.");
|
||||
|
||||
int columnCount = 0;
|
||||
DataGridColumn dataGridColumn = this.ItemsInternal[fromColumnIndex];
|
||||
|
||||
while (dataGridColumn != this.ItemsInternal[toColumnIndex])
|
||||
{
|
||||
dataGridColumn = GetNextVisibleColumn(dataGridColumn);
|
||||
Debug.Assert(dataGridColumn != null, "Expected non-null dataGridColumn.");
|
||||
columnCount++;
|
||||
}
|
||||
|
||||
return columnCount;
|
||||
}
|
||||
|
||||
internal IEnumerable<DataGridColumn> GetVisibleColumns()
|
||||
{
|
||||
Predicate<DataGridColumn> filter = column => column.IsVisible;
|
||||
return GetDisplayedColumns(filter);
|
||||
}
|
||||
|
||||
internal IEnumerable<DataGridColumn> GetVisibleFrozenColumns()
|
||||
{
|
||||
Predicate<DataGridColumn> filter = column => column.IsVisible && column.IsFrozen;
|
||||
return GetDisplayedColumns(filter);
|
||||
}
|
||||
|
||||
internal double GetVisibleFrozenEdgedColumnsWidth()
|
||||
{
|
||||
double visibleFrozenColumnsWidth = 0;
|
||||
for (int columnIndex = 0; columnIndex < this.ItemsInternal.Count; columnIndex++)
|
||||
{
|
||||
if (this.ItemsInternal[columnIndex].IsVisible && this.ItemsInternal[columnIndex].IsFrozen)
|
||||
{
|
||||
visibleFrozenColumnsWidth += this.ItemsInternal[columnIndex].ActualWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return visibleFrozenColumnsWidth;
|
||||
}
|
||||
|
||||
internal IEnumerable<DataGridColumn> GetVisibleScrollingColumns()
|
||||
{
|
||||
Predicate<DataGridColumn> filter = column => column.IsVisible && !column.IsFrozen;
|
||||
return GetDisplayedColumns(filter);
|
||||
}
|
||||
|
||||
private void RemoveItemPrivate(int columnIndex, bool isSpacer)
|
||||
{
|
||||
Debug.Assert(_owningGrid != null, "Expected non-null owning DataGrid.");
|
||||
try
|
||||
{
|
||||
_owningGrid.NoCurrentCellChangeCount++;
|
||||
|
||||
if (_owningGrid.InDisplayIndexAdjustments)
|
||||
{
|
||||
// We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes.
|
||||
throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes();
|
||||
}
|
||||
|
||||
int columnIndexWithFiller = columnIndex;
|
||||
if (!isSpacer && this.RowGroupSpacerColumn.IsRepresented)
|
||||
{
|
||||
columnIndexWithFiller++;
|
||||
}
|
||||
|
||||
Debug.Assert(columnIndexWithFiller >= 0 && columnIndexWithFiller < this.ItemsInternal.Count, "Unexpected columnIndexWithFiller value.");
|
||||
|
||||
DataGridColumn dataGridColumn = this.ItemsInternal[columnIndexWithFiller];
|
||||
DataGridCellCoordinates newCurrentCellCoordinates = _owningGrid.OnRemovingColumn(dataGridColumn);
|
||||
this.ItemsInternal.RemoveAt(columnIndexWithFiller);
|
||||
if (dataGridColumn.IsVisible)
|
||||
{
|
||||
this.VisibleEdgedColumnsWidth -= dataGridColumn.ActualWidth;
|
||||
}
|
||||
|
||||
dataGridColumn.OwningGrid = null;
|
||||
dataGridColumn.RemoveEditingElement();
|
||||
|
||||
// continue with the base remove
|
||||
_owningGrid.OnRemovedColumn_PreNotification(dataGridColumn);
|
||||
_owningGrid.OnColumnCollectionChanged_PreNotification(false /*columnsGrew*/);
|
||||
if (!isSpacer)
|
||||
{
|
||||
base.RemoveItem(columnIndex);
|
||||
}
|
||||
|
||||
_owningGrid.OnRemovedColumn_PostNotification(newCurrentCellCoordinates);
|
||||
_owningGrid.OnColumnCollectionChanged_PostNotification(false /*columnsGrew*/);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_owningGrid.NoCurrentCellChangeCount--;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
internal bool Debug_VerifyColumnDisplayIndexes()
|
||||
{
|
||||
for (int columnDisplayIndex = 0; columnDisplayIndex < this.ItemsInternal.Count; columnDisplayIndex++)
|
||||
{
|
||||
if (GetColumnAtDisplayIndex(columnDisplayIndex) == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void Debug_PrintColumns()
|
||||
{
|
||||
foreach (DataGridColumn column in this.ItemsInternal)
|
||||
{
|
||||
Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} {2}", column.Header, column.Index, column.DisplayIndex));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// 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>
|
||||
/// Provides data for <see cref="DataGrid"/> column-related events.
|
||||
/// </summary>
|
||||
public class DataGridColumnEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridColumnEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="column">The column that the event occurs for.</param>
|
||||
public DataGridColumnEventArgs(DataGridColumn column)
|
||||
{
|
||||
if (column == null)
|
||||
{
|
||||
throw new ArgumentNullException("column");
|
||||
}
|
||||
|
||||
this.Column = column;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the column that the event occurs for.
|
||||
/// </summary>
|
||||
public DataGridColumn Column
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,93 @@
|
|||
// 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.Foundation;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Primitives
|
||||
{
|
||||
internal class DataGridColumnHeaderInteractionInfo
|
||||
{
|
||||
internal Pointer CapturedPointer
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal DataGridColumn DragColumn
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal DataGridColumnHeader.DragMode DragMode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal uint DragPointerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal Point? DragStart
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal double FrozenColumnsWidth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal bool HasUserInteraction
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.DragMode != DataGridColumnHeader.DragMode.None;
|
||||
}
|
||||
}
|
||||
|
||||
internal Point? LastPointerPositionHeaders
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal CoreCursor OriginalCursor
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal double OriginalHorizontalOffset
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal double OriginalWidth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal Point? PressedPointerPositionHeaders
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal uint ResizePointerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,419 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Used within the template of a <see cref="DataGrid"/> to specify the
|
||||
/// location in the control's visual tree where the column headers are to be added.
|
||||
/// </summary>
|
||||
public sealed class DataGridColumnHeadersPresenter : Panel
|
||||
{
|
||||
private Control _dragIndicator;
|
||||
private Control _dropLocationIndicator;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets which column is currently being dragged.
|
||||
/// </summary>
|
||||
internal DataGridColumn DragColumn
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current drag indicator control. This value is null if no column is being dragged.
|
||||
/// </summary>
|
||||
internal Control DragIndicator
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dragIndicator;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != _dragIndicator)
|
||||
{
|
||||
if (this.Children.Contains(_dragIndicator))
|
||||
{
|
||||
this.Children.Remove(_dragIndicator);
|
||||
}
|
||||
|
||||
_dragIndicator = value;
|
||||
if (_dragIndicator != null)
|
||||
{
|
||||
this.Children.Add(_dragIndicator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the distance, in pixels, that the DragIndicator should be positioned away from the corresponding DragColumn.
|
||||
/// </summary>
|
||||
internal double DragIndicatorOffset
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the drop location indicator control. This value is null if no column is being dragged.
|
||||
/// </summary>
|
||||
internal Control DropLocationIndicator
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dropLocationIndicator;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != _dropLocationIndicator)
|
||||
{
|
||||
if (this.Children.Contains(_dropLocationIndicator))
|
||||
{
|
||||
this.Children.Remove(_dropLocationIndicator);
|
||||
}
|
||||
|
||||
_dropLocationIndicator = value;
|
||||
if (_dropLocationIndicator != null)
|
||||
{
|
||||
this.Children.Add(_dropLocationIndicator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the distance, in pixels, that the drop location indicator should be positioned away from the left edge
|
||||
/// of the ColumnsHeaderPresenter.
|
||||
/// </summary>
|
||||
internal double DropLocationIndicatorOffset
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal DataGrid OwningGrid
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arranges the content of the <see cref="DataGridColumnHeadersPresenter"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The actual size used by the <see cref="DataGridColumnHeadersPresenter"/>.
|
||||
/// </returns>
|
||||
/// <param name="finalSize">
|
||||
/// The final area within the parent that this element should use to arrange itself and its children.
|
||||
/// </param>
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (this.OwningGrid == null)
|
||||
{
|
||||
return base.ArrangeOverride(finalSize);
|
||||
}
|
||||
|
||||
if (this.OwningGrid.AutoSizingColumns)
|
||||
{
|
||||
// When we initially load an auto-column, we have to wait for all the rows to be measured
|
||||
// before we know its final desired size. We need to trigger a new round of measures now
|
||||
// that the final sizes have been calculated.
|
||||
this.OwningGrid.AutoSizingColumns = false;
|
||||
return base.ArrangeOverride(finalSize);
|
||||
}
|
||||
|
||||
double dragIndicatorLeftEdge = 0;
|
||||
double frozenLeftEdge = 0;
|
||||
double scrollingLeftEdge = -this.OwningGrid.HorizontalOffset;
|
||||
foreach (DataGridColumn dataGridColumn in this.OwningGrid.ColumnsInternal.GetVisibleColumns())
|
||||
{
|
||||
DataGridColumnHeader columnHeader = dataGridColumn.HeaderCell;
|
||||
Debug.Assert(columnHeader.OwningColumn == dataGridColumn, "Expected columnHeader owned by dataGridColumn.");
|
||||
|
||||
if (dataGridColumn.IsFrozen)
|
||||
{
|
||||
columnHeader.Arrange(new Rect(frozenLeftEdge, 0, dataGridColumn.LayoutRoundedWidth, finalSize.Height));
|
||||
columnHeader.Clip = null; // The layout system could have clipped this because it's not aware of our render transform
|
||||
if (this.DragColumn == dataGridColumn && this.DragIndicator != null)
|
||||
{
|
||||
dragIndicatorLeftEdge = frozenLeftEdge + this.DragIndicatorOffset;
|
||||
}
|
||||
|
||||
frozenLeftEdge += dataGridColumn.ActualWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
columnHeader.Arrange(new Rect(scrollingLeftEdge, 0, dataGridColumn.LayoutRoundedWidth, finalSize.Height));
|
||||
EnsureColumnHeaderClip(columnHeader, dataGridColumn.ActualWidth, finalSize.Height, frozenLeftEdge, scrollingLeftEdge);
|
||||
if (this.DragColumn == dataGridColumn && this.DragIndicator != null)
|
||||
{
|
||||
dragIndicatorLeftEdge = scrollingLeftEdge + this.DragIndicatorOffset;
|
||||
}
|
||||
}
|
||||
|
||||
scrollingLeftEdge += dataGridColumn.ActualWidth;
|
||||
}
|
||||
|
||||
if (this.DragColumn != null)
|
||||
{
|
||||
if (this.DragIndicator != null)
|
||||
{
|
||||
this.EnsureColumnReorderingClip(this.DragIndicator, finalSize.Height, frozenLeftEdge, dragIndicatorLeftEdge);
|
||||
this.DragIndicator.Arrange(new Rect(dragIndicatorLeftEdge, 0, this.DragIndicator.ActualWidth, this.DragIndicator.ActualHeight));
|
||||
}
|
||||
|
||||
if (this.DropLocationIndicator != null)
|
||||
{
|
||||
this.EnsureColumnReorderingClip(this.DropLocationIndicator, finalSize.Height, frozenLeftEdge, this.DropLocationIndicatorOffset);
|
||||
this.DropLocationIndicator.Arrange(new Rect(this.DropLocationIndicatorOffset, 0, this.DropLocationIndicator.ActualWidth, this.DropLocationIndicator.ActualHeight));
|
||||
}
|
||||
}
|
||||
|
||||
// Arrange filler
|
||||
this.OwningGrid.OnFillerColumnWidthNeeded(finalSize.Width);
|
||||
DataGridFillerColumn fillerColumn = this.OwningGrid.ColumnsInternal.FillerColumn;
|
||||
if (fillerColumn.FillerWidth > 0)
|
||||
{
|
||||
fillerColumn.HeaderCell.Visibility = Visibility.Visible;
|
||||
fillerColumn.HeaderCell.Arrange(new Rect(scrollingLeftEdge, 0, fillerColumn.FillerWidth, finalSize.Height));
|
||||
}
|
||||
else
|
||||
{
|
||||
fillerColumn.HeaderCell.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
// This needs to be updated after the filler column is configured
|
||||
DataGridColumn lastVisibleColumn = this.OwningGrid.ColumnsInternal.LastVisibleColumn;
|
||||
if (lastVisibleColumn != null)
|
||||
{
|
||||
lastVisibleColumn.HeaderCell.UpdateSeparatorVisibility(lastVisibleColumn);
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
private static void EnsureColumnHeaderClip(DataGridColumnHeader columnHeader, double width, double height, double frozenLeftEdge, double columnHeaderLeftEdge)
|
||||
{
|
||||
// Clip the cell only if it's scrolled under frozen columns. Unfortunately, we need to clip in this case
|
||||
// because cells could be transparent
|
||||
if (frozenLeftEdge > columnHeaderLeftEdge)
|
||||
{
|
||||
RectangleGeometry rg = new RectangleGeometry();
|
||||
double xClip = Math.Min(width, frozenLeftEdge - columnHeaderLeftEdge);
|
||||
rg.Rect = new Rect(xClip, 0, width - xClip, height);
|
||||
columnHeader.Clip = rg;
|
||||
}
|
||||
else
|
||||
{
|
||||
columnHeader.Clip = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clips the DragIndicator and DropLocationIndicator controls according to current ColumnHeaderPresenter constraints.
|
||||
/// </summary>
|
||||
/// <param name="control">The DragIndicator or DropLocationIndicator</param>
|
||||
/// <param name="height">The available height</param>
|
||||
/// <param name="frozenColumnsWidth">The width of the frozen column region</param>
|
||||
/// <param name="controlLeftEdge">The left edge of the control to clip</param>
|
||||
private void EnsureColumnReorderingClip(Control control, double height, double frozenColumnsWidth, double controlLeftEdge)
|
||||
{
|
||||
double leftEdge = 0;
|
||||
double rightEdge = this.OwningGrid.CellsWidth;
|
||||
double width = control.ActualWidth;
|
||||
if (this.DragColumn.IsFrozen)
|
||||
{
|
||||
// If we're dragging a frozen column, we want to clip the corresponding DragIndicator control when it goes
|
||||
// into the scrolling columns region, but not the DropLocationIndicator.
|
||||
if (control == this.DragIndicator)
|
||||
{
|
||||
rightEdge = Math.Min(rightEdge, frozenColumnsWidth);
|
||||
}
|
||||
}
|
||||
else if (this.OwningGrid.FrozenColumnCount > 0)
|
||||
{
|
||||
// If we're dragging a scrolling column, we want to clip both the DragIndicator and the DropLocationIndicator
|
||||
// controls when they go into the frozen column range.
|
||||
leftEdge = frozenColumnsWidth;
|
||||
}
|
||||
|
||||
RectangleGeometry rg = null;
|
||||
if (leftEdge > controlLeftEdge)
|
||||
{
|
||||
rg = new RectangleGeometry();
|
||||
double xClip = Math.Min(width, leftEdge - controlLeftEdge);
|
||||
rg.Rect = new Rect(xClip, 0, width - xClip, height);
|
||||
}
|
||||
|
||||
if (controlLeftEdge + width >= rightEdge)
|
||||
{
|
||||
if (rg == null)
|
||||
{
|
||||
rg = new RectangleGeometry();
|
||||
}
|
||||
|
||||
rg.Rect = new Rect(rg.Rect.X, rg.Rect.Y, Math.Max(0, rightEdge - controlLeftEdge - rg.Rect.X), height);
|
||||
}
|
||||
|
||||
control.Clip = rg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures the children of a <see cref="DataGridColumnHeadersPresenter"/> to
|
||||
/// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)"/> pass.
|
||||
/// </summary>
|
||||
/// <param name="availableSize">
|
||||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The size that the <see cref="DataGridColumnHeadersPresenter"/> determines it needs during layout, based on its calculations of child object allocated sizes.
|
||||
/// </returns>
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (this.OwningGrid == null)
|
||||
{
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
if (!this.OwningGrid.AreColumnHeadersVisible)
|
||||
{
|
||||
return new Size(0.0, 0.0);
|
||||
}
|
||||
|
||||
double height = this.OwningGrid.ColumnHeaderHeight;
|
||||
bool autoSizeHeight;
|
||||
if (double.IsNaN(height))
|
||||
{
|
||||
// No explicit height values were set so we can autosize
|
||||
height = 0;
|
||||
autoSizeHeight = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
autoSizeHeight = false;
|
||||
}
|
||||
|
||||
double totalDisplayWidth = 0;
|
||||
this.OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
|
||||
DataGridColumn lastVisibleColumn = this.OwningGrid.ColumnsInternal.LastVisibleColumn;
|
||||
foreach (DataGridColumn column in this.OwningGrid.ColumnsInternal.GetVisibleColumns())
|
||||
{
|
||||
// Measure each column header
|
||||
bool autoGrowWidth = column.Width.IsAuto || column.Width.IsSizeToHeader;
|
||||
DataGridColumnHeader columnHeader = column.HeaderCell;
|
||||
if (column != lastVisibleColumn)
|
||||
{
|
||||
columnHeader.UpdateSeparatorVisibility(lastVisibleColumn);
|
||||
}
|
||||
|
||||
// If we're not using star sizing or the current column can't be resized,
|
||||
// then just set the display width according to the column's desired width
|
||||
if (!this.OwningGrid.UsesStarSizing || (!column.ActualCanUserResize && !column.Width.IsStar))
|
||||
{
|
||||
// In the edge-case where we're given infinite width and we have star columns, the
|
||||
// star columns grow to their predefined limit of 10,000 (or their MaxWidth)
|
||||
double newDisplayWidth = column.Width.IsStar ?
|
||||
Math.Min(column.ActualMaxWidth, DataGrid.DATAGRID_maximumStarColumnWidth) :
|
||||
Math.Max(column.ActualMinWidth, Math.Min(column.ActualMaxWidth, column.Width.DesiredValue));
|
||||
column.SetWidthDisplayValue(newDisplayWidth);
|
||||
}
|
||||
|
||||
// If we're auto-growing the column based on the header content, we want to measure it at its maximum value
|
||||
if (autoGrowWidth)
|
||||
{
|
||||
columnHeader.Measure(new Size(column.ActualMaxWidth, double.PositiveInfinity));
|
||||
this.OwningGrid.AutoSizeColumn(column, columnHeader.DesiredSize.Width);
|
||||
column.ComputeLayoutRoundedWidth(totalDisplayWidth);
|
||||
}
|
||||
else if (!this.OwningGrid.UsesStarSizing)
|
||||
{
|
||||
column.ComputeLayoutRoundedWidth(totalDisplayWidth);
|
||||
columnHeader.Measure(new Size(column.LayoutRoundedWidth, double.PositiveInfinity));
|
||||
}
|
||||
|
||||
// We need to track the largest height in order to auto-size
|
||||
if (autoSizeHeight)
|
||||
{
|
||||
height = Math.Max(height, columnHeader.DesiredSize.Height);
|
||||
}
|
||||
|
||||
totalDisplayWidth += column.ActualWidth;
|
||||
}
|
||||
|
||||
// If we're using star sizing (and we're not waiting for an auto-column to finish growing)
|
||||
// then we will resize all the columns to fit the available space.
|
||||
if (this.OwningGrid.UsesStarSizing && !this.OwningGrid.AutoSizingColumns)
|
||||
{
|
||||
double adjustment = double.IsPositiveInfinity(availableSize.Width) ? this.OwningGrid.CellsWidth : availableSize.Width - totalDisplayWidth;
|
||||
this.OwningGrid.AdjustColumnWidths(0, adjustment, false);
|
||||
|
||||
// Since we didn't know the final widths of the columns until we resized,
|
||||
// we waited until now to measure each header
|
||||
double leftEdge = 0;
|
||||
foreach (var column in this.OwningGrid.ColumnsInternal.GetVisibleColumns())
|
||||
{
|
||||
column.ComputeLayoutRoundedWidth(leftEdge);
|
||||
column.HeaderCell.Measure(new Size(column.LayoutRoundedWidth, double.PositiveInfinity));
|
||||
if (autoSizeHeight)
|
||||
{
|
||||
height = Math.Max(height, column.HeaderCell.DesiredSize.Height);
|
||||
}
|
||||
|
||||
leftEdge += column.ActualWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the filler column if it's not represented. We won't know whether we need it or not until Arrange
|
||||
DataGridFillerColumn fillerColumn = this.OwningGrid.ColumnsInternal.FillerColumn;
|
||||
if (!fillerColumn.IsRepresented)
|
||||
{
|
||||
Debug.Assert(!this.Children.Contains(fillerColumn.HeaderCell), "Unexpected parent for filler column header cell.");
|
||||
fillerColumn.HeaderCell.SeparatorVisibility = Visibility.Collapsed;
|
||||
this.Children.Insert(this.OwningGrid.ColumnsInternal.Count, fillerColumn.HeaderCell);
|
||||
fillerColumn.IsRepresented = true;
|
||||
|
||||
// Optimize for the case where we don't need the filler cell
|
||||
fillerColumn.HeaderCell.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
fillerColumn.HeaderCell.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
|
||||
if (this.DragIndicator != null)
|
||||
{
|
||||
this.DragIndicator.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
}
|
||||
|
||||
if (this.DropLocationIndicator != null)
|
||||
{
|
||||
this.DropLocationIndicator.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
}
|
||||
|
||||
this.OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
|
||||
return new Size(this.OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
|
||||
/// </summary>
|
||||
/// <returns>An automation peer for this <see cref="DataGridColumnHeadersPresenter"/>.</returns>
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new DataGridColumnHeadersPresenterAutomationPeer(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// 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.ComponentModel;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data for the <see cref="E:Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.ColumnReordering" /> event.
|
||||
/// </summary>
|
||||
public class DataGridColumnReorderingEventArgs : CancelEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridColumnReorderingEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dataGridColumn">The column that the event occurs for.</param>
|
||||
public DataGridColumnReorderingEventArgs(DataGridColumn dataGridColumn)
|
||||
{
|
||||
this.Column = dataGridColumn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the column being moved.
|
||||
/// </summary>
|
||||
public DataGridColumn Column
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the popup indicator displayed while dragging. If null and Handled = true, then do not display a tooltip.
|
||||
/// </summary>
|
||||
public Control DragIndicator
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Control to display at the insertion position. If null and Handled = true, then do not display an insertion indicator.
|
||||
/// </summary>
|
||||
public Control DropLocationIndicator
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,738 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Microsoft.Toolkit.Uwp.UI.Utilities;
|
||||
using Microsoft.Toolkit.Uwp.Utilities;
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="DataGrid"/> column that hosts textual content in its cells. In edit mode data can be changed to a value from a collection hosted in a ComboBox.
|
||||
/// </summary>
|
||||
[StyleTypedProperty(Property = "ElementStyle", StyleTargetType = typeof(TextBlock))]
|
||||
[StyleTypedProperty(Property = "EditingElementStyle", StyleTargetType = typeof(ComboBox))]
|
||||
public class DataGridComboBoxColumn : DataGridBoundColumn
|
||||
{
|
||||
private const string DATAGRIDCOMBOBOXCOLUMN_fontFamilyName = "FontFamily";
|
||||
private const string DATAGRIDCOMBOBOXCOLUMN_fontSizeName = "FontSize";
|
||||
private const string DATAGRIDCOMBOBOXCOLUMN_fontStyleName = "FontStyle";
|
||||
private const string DATAGRIDCOMBOBOXCOLUMN_fontWeightName = "FontWeight";
|
||||
private const string DATAGRIDCOMBOBOXCOLUMN_foregroundName = "Foreground";
|
||||
private const string DATAGRIDCOMBOBOXCOLUMN_itemsSourceName = "ItemsSource";
|
||||
private const string DATAGRIDCOMBOBOXCOLUMN_displayMemberPathName = "DisplayMemberPath";
|
||||
private const double DATAGRIDCOMBOBOXCOLUMN_leftMargin = 12.0;
|
||||
private const double DATAGRIDCOMBOBOXCOLUMN_rightMargin = 12.0;
|
||||
|
||||
private double? _fontSize;
|
||||
private DataGrid _owningGrid;
|
||||
private FontStyle? _fontStyle;
|
||||
private FontWeight? _fontWeight;
|
||||
private Brush _foreground;
|
||||
private HashSet<object> _notifyingDataItems;
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the ItemsSource dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ItemsSourceProperty =
|
||||
DependencyProperty.Register(
|
||||
DATAGRIDCOMBOBOXCOLUMN_itemsSourceName,
|
||||
typeof(IEnumerable),
|
||||
typeof(DataGridComboBoxColumn),
|
||||
new PropertyMetadata(default(IEnumerable), OnItemSourcePropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a collection that is used to generate the content of the ComboBox while in editing mode.
|
||||
/// </summary>
|
||||
public IEnumerable ItemsSource
|
||||
{
|
||||
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
|
||||
set { SetValue(ItemsSourceProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnItemSourcePropertyChanged(DependencyObject comboBoxDependencyObject, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var comboColumn = comboBoxDependencyObject as DataGridComboBoxColumn;
|
||||
comboColumn.NotifyPropertyChanged(DATAGRIDCOMBOBOXCOLUMN_itemsSourceName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the DisplayMemberPath dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty DisplayMemberPathProperty =
|
||||
DependencyProperty.Register(
|
||||
DATAGRIDCOMBOBOXCOLUMN_displayMemberPathName,
|
||||
typeof(string),
|
||||
typeof(DataGridComboBoxColumn),
|
||||
new PropertyMetadata(default(string)));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name or path of the property that is displayed in the ComboBox.
|
||||
/// </summary>
|
||||
public string DisplayMemberPath
|
||||
{
|
||||
get { return (string)GetValue(DisplayMemberPathProperty); }
|
||||
set { SetValue(DisplayMemberPathProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font name.
|
||||
/// </summary>
|
||||
public FontFamily FontFamily
|
||||
{
|
||||
get { return (FontFamily)GetValue(FontFamilyProperty); }
|
||||
set { SetValue(FontFamilyProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the FontFamily dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FontFamilyProperty =
|
||||
DependencyProperty.Register(
|
||||
DATAGRIDCOMBOBOXCOLUMN_fontFamilyName,
|
||||
typeof(FontFamily),
|
||||
typeof(DataGridComboBoxColumn),
|
||||
new PropertyMetadata(null, OnFontFamilyPropertyChanged));
|
||||
|
||||
private static void OnFontFamilyPropertyChanged(DependencyObject comboBoxColumnDependencyObject, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var comboColumn = comboBoxColumnDependencyObject as DataGridComboBoxColumn;
|
||||
comboColumn.NotifyPropertyChanged(DATAGRIDCOMBOBOXCOLUMN_fontFamilyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font size.
|
||||
/// </summary>
|
||||
// Use DefaultValue here so undo in the Designer will set this to NaN
|
||||
[DefaultValue(double.NaN)]
|
||||
public double FontSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fontSize ?? double.NaN;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_fontSize != value)
|
||||
{
|
||||
_fontSize = value;
|
||||
NotifyPropertyChanged(DATAGRIDCOMBOBOXCOLUMN_fontSizeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font style.
|
||||
/// </summary>
|
||||
public FontStyle FontStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fontStyle ?? FontStyle.Normal;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_fontStyle != value)
|
||||
{
|
||||
_fontStyle = value;
|
||||
NotifyPropertyChanged(DATAGRIDCOMBOBOXCOLUMN_fontStyleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font weight or thickness.
|
||||
/// </summary>
|
||||
public FontWeight FontWeight
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fontWeight ?? FontWeights.Normal;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!_fontWeight.HasValue || _fontWeight.Value.Weight != value.Weight)
|
||||
{
|
||||
_fontWeight = value;
|
||||
NotifyPropertyChanged(DATAGRIDCOMBOBOXCOLUMN_fontWeightName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a brush that describes the foreground of the column cells.
|
||||
/// </summary>
|
||||
public Brush Foreground
|
||||
{
|
||||
get
|
||||
{
|
||||
return _foreground;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_foreground != value)
|
||||
{
|
||||
_foreground = value;
|
||||
NotifyPropertyChanged(DATAGRIDCOMBOBOXCOLUMN_foregroundName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="T:Windows.UI.Xaml.Controls.ComboBox"/> control that is bound to the column's ItemsSource collection.
|
||||
/// </summary>
|
||||
/// <param name="cell">The cell that will contain the generated element.</param>
|
||||
/// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
|
||||
/// <returns>A new <see cref="T:Windows.UI.Xaml.Controls.ComboBox"/> control that is bound to the column's ItemsSource collection.</returns>
|
||||
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
EnsureColumnBinding(dataItem);
|
||||
|
||||
EnsureDisplayMemberPathExists();
|
||||
|
||||
EnsureItemsSourceBinding();
|
||||
|
||||
EnsureColumnTypeAgreement(dataItem);
|
||||
|
||||
var comboBox = new ComboBox
|
||||
{
|
||||
Margin = default(Thickness),
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
if (dataItem != null)
|
||||
{
|
||||
var value = TypeHelper.GetNestedPropertyValue(dataItem, Binding.Path.Path);
|
||||
|
||||
var selection = !string.IsNullOrEmpty(DisplayMemberPath)
|
||||
? ItemsSource?.Cast<object>().FirstOrDefault(x => TypeHelper.GetNestedPropertyValue(x, Binding.GetBindingPropertyName()).Equals(value))
|
||||
: ItemsSource?.Cast<object>().FirstOrDefault(x => x.Equals(value));
|
||||
|
||||
comboBox.SelectedItem = selection;
|
||||
}
|
||||
|
||||
var itemsSourceBinding = new Binding
|
||||
{
|
||||
Source = this,
|
||||
Path = new PropertyPath(DATAGRIDCOMBOBOXCOLUMN_itemsSourceName)
|
||||
};
|
||||
|
||||
var displayMemberPathBinding = new Binding
|
||||
{
|
||||
Source = this,
|
||||
Path = new PropertyPath(DATAGRIDCOMBOBOXCOLUMN_displayMemberPathName)
|
||||
};
|
||||
|
||||
comboBox.SetBinding(ComboBox.ItemsSourceProperty, itemsSourceBinding);
|
||||
|
||||
comboBox.SetBinding(ComboBox.DisplayMemberPathProperty, displayMemberPathBinding);
|
||||
|
||||
if (DependencyProperty.UnsetValue != ReadLocalValue(DataGridComboBoxColumn.FontFamilyProperty))
|
||||
{
|
||||
comboBox.FontFamily = FontFamily;
|
||||
}
|
||||
|
||||
if (_fontSize.HasValue)
|
||||
{
|
||||
comboBox.FontSize = _fontSize.Value;
|
||||
}
|
||||
|
||||
if (_fontStyle.HasValue)
|
||||
{
|
||||
comboBox.FontStyle = _fontStyle.Value;
|
||||
}
|
||||
|
||||
if (_fontWeight.HasValue)
|
||||
{
|
||||
comboBox.FontWeight = _fontWeight.Value;
|
||||
}
|
||||
|
||||
RefreshForeground(comboBox, (cell != null & cell.OwningRow != null) ? cell.OwningRow.ComputedForeground : null);
|
||||
|
||||
comboBox.SelectionChanged += (sender, args) =>
|
||||
{
|
||||
var item = args.AddedItems.FirstOrDefault();
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
var newValue = !string.IsNullOrEmpty(DisplayMemberPath)
|
||||
? item.GetType().GetProperty(Binding.GetBindingPropertyName())?.GetValue(item)
|
||||
: item;
|
||||
|
||||
TypeHelper.SetNestedPropertyValue(ref dataItem, newValue, Binding.Path.Path);
|
||||
}
|
||||
};
|
||||
|
||||
return comboBox;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only <see cref="T:Windows.UI.Xaml.Controls.TextBlock"/> element that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.
|
||||
/// </summary>
|
||||
/// <param name="cell">The cell that will contain the generated element.</param>
|
||||
/// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
|
||||
/// <returns>A new, read-only <see cref="T:Windows.UI.Xaml.Controls.TextBlock"/> element that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.</returns>
|
||||
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
EnsureColumnBinding(dataItem);
|
||||
EnsureColumnTypeAgreement(dataItem);
|
||||
EnsureDisplayMemberPathExists();
|
||||
EnsureItemsSourceBinding();
|
||||
|
||||
var textBlockElement = new TextBlock
|
||||
{
|
||||
Margin = new Thickness(DATAGRIDCOMBOBOXCOLUMN_leftMargin, 0.0, DATAGRIDCOMBOBOXCOLUMN_rightMargin, 0.0),
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
if (DependencyProperty.UnsetValue != ReadLocalValue(DataGridComboBoxColumn.FontFamilyProperty))
|
||||
{
|
||||
textBlockElement.FontFamily = FontFamily;
|
||||
}
|
||||
|
||||
if (_fontSize.HasValue)
|
||||
{
|
||||
textBlockElement.FontSize = _fontSize.Value;
|
||||
}
|
||||
|
||||
if (_fontStyle.HasValue)
|
||||
{
|
||||
textBlockElement.FontStyle = _fontStyle.Value;
|
||||
}
|
||||
|
||||
if (_fontWeight.HasValue)
|
||||
{
|
||||
textBlockElement.FontWeight = _fontWeight.Value;
|
||||
}
|
||||
|
||||
RefreshForeground(textBlockElement, (cell != null & cell.OwningRow != null) ? cell.OwningRow.ComputedForeground : null);
|
||||
|
||||
if (Binding != null && EnsureOwningGrid())
|
||||
{
|
||||
if (string.IsNullOrEmpty(DisplayMemberPath))
|
||||
{
|
||||
textBlockElement.SetBinding(TextBlock.TextProperty, Binding);
|
||||
}
|
||||
else
|
||||
{
|
||||
textBlockElement.Text = GetDisplayValue(dataItem);
|
||||
|
||||
HookDataItemPropertyChanged(dataItem);
|
||||
}
|
||||
}
|
||||
|
||||
return textBlockElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Causes the column cell being edited to revert to the specified value.
|
||||
/// </summary>
|
||||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
||||
/// <param name="uneditedValue">The previous, unedited value in the cell being edited.</param>
|
||||
protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
|
||||
{
|
||||
var comboBox = editingElement as ComboBox;
|
||||
|
||||
if (comboBox != null)
|
||||
{
|
||||
if (uneditedValue != null)
|
||||
{
|
||||
var property = uneditedValue.GetType().GetNestedProperty(Binding.GetBindingPropertyName());
|
||||
|
||||
if (property == null)
|
||||
{
|
||||
comboBox.SelectedItem = uneditedValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = TypeHelper.GetNestedPropertyValue(uneditedValue, Binding.GetBindingPropertyName());
|
||||
var selection = ItemsSource?.Cast<object>().FirstOrDefault(x => TypeHelper.GetNestedPropertyValue(x, Binding.GetBindingPropertyName()).Equals(value));
|
||||
|
||||
comboBox.SelectedItem = selection;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
comboBox.SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the cell in the column enters editing mode.
|
||||
/// </summary>
|
||||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
||||
/// <param name="editingEventArgs">Information about the user gesture that is causing a cell to enter editing mode.</param>
|
||||
/// <returns>The unedited value. </returns>
|
||||
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
|
||||
{
|
||||
return (editingElement as ComboBox)?.SelectedItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the DataGrid control when this column asks for its elements to be updated, because a property changed.
|
||||
/// </summary>
|
||||
protected internal override void RefreshCellContent(FrameworkElement element, Brush computedRowForeground, string propertyName)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
var comboBox = element as ComboBox;
|
||||
if (comboBox == null)
|
||||
{
|
||||
var textBlock = element as TextBlock;
|
||||
if (textBlock == null)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueIsNotAnInstanceOfEitherOr(nameof(element), typeof(ComboBox), typeof(TextBlock));
|
||||
}
|
||||
|
||||
if (propertyName == DATAGRIDCOMBOBOXCOLUMN_fontFamilyName)
|
||||
{
|
||||
textBlock.FontFamily = FontFamily;
|
||||
}
|
||||
else if (propertyName == DATAGRIDCOMBOBOXCOLUMN_fontSizeName)
|
||||
{
|
||||
SetTextFontSize(textBlock, TextBlock.FontSizeProperty);
|
||||
}
|
||||
else if (propertyName == DATAGRIDCOMBOBOXCOLUMN_fontStyleName)
|
||||
{
|
||||
textBlock.FontStyle = FontStyle;
|
||||
}
|
||||
else if (propertyName == DATAGRIDCOMBOBOXCOLUMN_fontWeightName)
|
||||
{
|
||||
textBlock.FontWeight = FontWeight;
|
||||
}
|
||||
else if (propertyName == DATAGRIDCOMBOBOXCOLUMN_foregroundName)
|
||||
{
|
||||
RefreshForeground(textBlock, computedRowForeground);
|
||||
}
|
||||
else if (propertyName == DATAGRIDCOMBOBOXCOLUMN_itemsSourceName)
|
||||
{
|
||||
OwningGrid.OnColumnBindingChanged(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FontFamily != null)
|
||||
{
|
||||
textBlock.FontFamily = FontFamily;
|
||||
}
|
||||
|
||||
SetTextFontSize(textBlock, TextBlock.FontSizeProperty);
|
||||
textBlock.FontStyle = FontStyle;
|
||||
textBlock.FontWeight = FontWeight;
|
||||
RefreshForeground(textBlock, computedRowForeground);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (propertyName == DATAGRIDCOMBOBOXCOLUMN_fontFamilyName)
|
||||
{
|
||||
comboBox.FontFamily = FontFamily;
|
||||
}
|
||||
else if (propertyName == DATAGRIDCOMBOBOXCOLUMN_fontSizeName)
|
||||
{
|
||||
SetTextFontSize(comboBox, ComboBox.FontSizeProperty);
|
||||
}
|
||||
else if (propertyName == DATAGRIDCOMBOBOXCOLUMN_fontStyleName)
|
||||
{
|
||||
comboBox.FontStyle = FontStyle;
|
||||
}
|
||||
else if (propertyName == DATAGRIDCOMBOBOXCOLUMN_fontWeightName)
|
||||
{
|
||||
comboBox.FontWeight = FontWeight;
|
||||
}
|
||||
else if (propertyName == DATAGRIDCOMBOBOXCOLUMN_foregroundName)
|
||||
{
|
||||
RefreshForeground(comboBox, computedRowForeground);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FontFamily != null)
|
||||
{
|
||||
comboBox.FontFamily = FontFamily;
|
||||
}
|
||||
|
||||
SetTextFontSize(comboBox, ComboBox.FontSizeProperty);
|
||||
comboBox.FontStyle = FontStyle;
|
||||
comboBox.FontWeight = FontWeight;
|
||||
RefreshForeground(comboBox, computedRowForeground);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the computed foreground of a row changed.
|
||||
/// </summary>
|
||||
protected internal override void RefreshForeground(FrameworkElement element, Brush computedRowForeground)
|
||||
{
|
||||
if (element is ComboBox comboBox)
|
||||
{
|
||||
RefreshForeground(comboBox, computedRowForeground);
|
||||
}
|
||||
else if (element is TextBlock textBlock)
|
||||
{
|
||||
RefreshForeground(textBlock, computedRowForeground);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshForeground(ComboBox comboBox, Brush computedRowForeground)
|
||||
{
|
||||
if (Foreground == null)
|
||||
{
|
||||
if (computedRowForeground != null)
|
||||
{
|
||||
comboBox.Foreground = computedRowForeground;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
comboBox.Foreground = Foreground;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshForeground(TextBlock textBlock, Brush computedRowForeground)
|
||||
{
|
||||
if (Foreground == null)
|
||||
{
|
||||
if (computedRowForeground != null)
|
||||
{
|
||||
textBlock.Foreground = computedRowForeground;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
textBlock.Foreground = Foreground;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTextFontSize(DependencyObject textElement, DependencyProperty fontSizeProperty)
|
||||
{
|
||||
double newFontSize = FontSize;
|
||||
if (double.IsNaN(newFontSize))
|
||||
{
|
||||
textElement.ClearValue(fontSizeProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
textElement.SetValue(fontSizeProperty, newFontSize);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnsureOwningGrid()
|
||||
{
|
||||
if (OwningGrid != null)
|
||||
{
|
||||
if (OwningGrid != _owningGrid)
|
||||
{
|
||||
_owningGrid = OwningGrid;
|
||||
_owningGrid.Columns.CollectionChanged += new NotifyCollectionChangedEventHandler(Columns_CollectionChanged);
|
||||
_owningGrid.LoadingRow += OwningGrid_LoadingRow;
|
||||
_owningGrid.UnloadingRow += OwningGrid_UnloadingRow;
|
||||
_owningGrid.CellEditEnded += OwningGrid_CellEditEnded;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OwningGrid_LoadingRow(object sender, DataGridRowEventArgs e)
|
||||
{
|
||||
HookDataItemPropertyChanged(e.Row.DataContext);
|
||||
SetDisplayMemberPathValue(e.Row);
|
||||
}
|
||||
|
||||
private void OwningGrid_UnloadingRow(object sender, DataGridRowEventArgs e)
|
||||
{
|
||||
UnhookDataItemPropertyChanged(e.Row.DataContext);
|
||||
}
|
||||
|
||||
private void OwningGrid_CellEditEnded(object sender, DataGridCellEditEndedEventArgs e)
|
||||
{
|
||||
SetDisplayMemberPathValue(e.Row);
|
||||
}
|
||||
|
||||
private void Columns_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (OwningGrid == null && _owningGrid != null)
|
||||
{
|
||||
_notifyingDataItems?.Clear();
|
||||
_owningGrid.Columns.CollectionChanged -= new NotifyCollectionChangedEventHandler(Columns_CollectionChanged);
|
||||
_owningGrid.LoadingRow -= OwningGrid_LoadingRow;
|
||||
_owningGrid.UnloadingRow -= this.OwningGrid_UnloadingRow;
|
||||
_owningGrid.CellEditEnded -= OwningGrid_CellEditEnded;
|
||||
_owningGrid = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDisplayMemberPathValue(DataGridRow row)
|
||||
{
|
||||
if (OwningGrid != null && !string.IsNullOrEmpty(DisplayMemberPath))
|
||||
{
|
||||
var textBlock = GetCellContent(row) as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
var displayValue = GetDisplayValue(row.DataContext);
|
||||
|
||||
textBlock.Text = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetDisplayValue(object dataItem)
|
||||
{
|
||||
if (Binding?.Path != null && dataItem != null)
|
||||
{
|
||||
var value = TypeHelper.GetNestedPropertyValue(dataItem, Binding.Path.Path);
|
||||
|
||||
var item = ItemsSource?.Cast<object>().FirstOrDefault(x => TypeHelper.GetNestedPropertyValue(x, Binding.GetBindingPropertyName()).Equals(value));
|
||||
|
||||
var displayValue = item?.GetType().GetProperty(DisplayMemberPath).GetValue(item) ?? string.Empty;
|
||||
|
||||
return displayValue as string ?? displayValue.ToString();
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private void EnsureColumnBinding(object dataItem)
|
||||
{
|
||||
if (Binding?.Path == null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Header as string))
|
||||
{
|
||||
throw DataGridError.DataGridComboBoxColumn.UnsetBinding(Header as string);
|
||||
}
|
||||
|
||||
throw DataGridError.DataGridComboBoxColumn.UnsetBinding(GetType());
|
||||
}
|
||||
|
||||
var property = dataItem?.GetType().GetNestedProperty(Binding?.Path?.Path);
|
||||
|
||||
if (property == null && dataItem != null)
|
||||
{
|
||||
throw DataGridError.DataGridComboBoxColumn.UnknownBindingPath(Binding, dataItem?.GetType());
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureColumnTypeAgreement(object dataItem)
|
||||
{
|
||||
if (string.IsNullOrEmpty(DisplayMemberPath))
|
||||
{
|
||||
var itemsSourceType = ItemsSource?.GetType().GetEnumerableItemType();
|
||||
var dataItemType = dataItem?.GetType().GetNestedPropertyType(Binding?.Path?.Path);
|
||||
|
||||
if (dataItemType != null && itemsSourceType != null && itemsSourceType != dataItemType)
|
||||
{
|
||||
throw DataGridError.DataGridComboBoxColumn.BindingTypeMismatch(dataItemType, itemsSourceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureDisplayMemberPathExists()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(DisplayMemberPath))
|
||||
{
|
||||
var type = ItemsSource?.GetItemType();
|
||||
|
||||
if (ItemsSource != null && !type.GetProperties().Any(x => x.Name.Equals(DisplayMemberPath)))
|
||||
{
|
||||
throw DataGridError.DataGridComboBoxColumn.UnknownDisplayMemberPath(DisplayMemberPath, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureItemsSourceBinding()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(DisplayMemberPath) && ItemsSource != null)
|
||||
{
|
||||
var item = ItemsSource.Cast<object>().FirstOrDefault();
|
||||
|
||||
if (item != null && !item.GetType().GetProperties().Any(y => y.Name.Equals(Binding.GetBindingPropertyName())))
|
||||
{
|
||||
throw DataGridError.DataGridComboBoxColumn.UnknownItemsSourcePath(Binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HookDataItemPropertyChanged(object dataItem)
|
||||
{
|
||||
if (Binding.Mode == BindingMode.OneTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var notifyingDataItem = dataItem as INotifyPropertyChanged;
|
||||
|
||||
if (notifyingDataItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_notifyingDataItems == null)
|
||||
{
|
||||
_notifyingDataItems = new HashSet<object>();
|
||||
}
|
||||
|
||||
if (!_notifyingDataItems.Contains(dataItem))
|
||||
{
|
||||
notifyingDataItem.PropertyChanged += DataItem_PropertyChanged;
|
||||
_notifyingDataItems.Add(dataItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnhookDataItemPropertyChanged(object dataItem)
|
||||
{
|
||||
if (_notifyingDataItems == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var notifyingDataItem = dataItem as INotifyPropertyChanged;
|
||||
|
||||
if (notifyingDataItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_notifyingDataItems.Contains(dataItem))
|
||||
{
|
||||
notifyingDataItem.PropertyChanged -= DataItem_PropertyChanged;
|
||||
_notifyingDataItems.Remove(dataItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void DataItem_PropertyChanged(object dataItem, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (this.OwningGrid != null && Binding?.Path != null && this.Binding.Path.Path == e.PropertyName)
|
||||
{
|
||||
var dataGridRow = OwningGrid.GetRowFromItem(dataItem);
|
||||
|
||||
if (dataGridRow != null && this.GetCellContent(dataGridRow) is TextBlock textBlockElement)
|
||||
{
|
||||
textBlockElement.Text = GetDisplayValue(dataItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,171 @@
|
|||
// 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 Microsoft.Toolkit.Uwp.UI.Automation.Peers;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Used within the template of a <see cref="DataGrid"/> to specify the location in the control's visual tree
|
||||
/// where the row details are to be added.
|
||||
/// </summary>
|
||||
public sealed class DataGridDetailsPresenter : Panel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the height of the content.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The height of the content.
|
||||
/// </returns>
|
||||
public double ContentHeight
|
||||
{
|
||||
get { return (double)GetValue(ContentHeightProperty); }
|
||||
set { SetValue(ContentHeightProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the ContentHeight dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ContentHeightProperty =
|
||||
DependencyProperty.Register(
|
||||
"ContentHeight",
|
||||
typeof(double),
|
||||
typeof(DataGridDetailsPresenter),
|
||||
new PropertyMetadata(0.0, OnContentHeightPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// ContentHeightProperty property changed handler.
|
||||
/// </summary>
|
||||
/// <param name="d">DataGridDetailsPresenter.</param>
|
||||
/// <param name="e">DependencyPropertyChangedEventArgs.</param>
|
||||
private static void OnContentHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
DataGridDetailsPresenter detailsPresenter = d as DataGridDetailsPresenter;
|
||||
detailsPresenter.InvalidateMeasure();
|
||||
}
|
||||
|
||||
private DataGrid OwningGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningRow != null)
|
||||
{
|
||||
return this.OwningRow.OwningGrid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridRow OwningRow
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arranges the content of the <see cref="T:System.Windows.Controls.Primitives.DataGridDetailsPresenter"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The actual size used by the <see cref="T:System.Windows.Controls.Primitives.DataGridDetailsPresenter"/>.
|
||||
/// </returns>
|
||||
/// <param name="finalSize">
|
||||
/// The final area within the parent that this element should use to arrange itself and its children.
|
||||
/// </param>
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (this.OwningGrid == null)
|
||||
{
|
||||
return base.ArrangeOverride(finalSize);
|
||||
}
|
||||
|
||||
double rowGroupSpacerWidth = this.OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value;
|
||||
double xClip = this.OwningGrid.AreRowGroupHeadersFrozen ? rowGroupSpacerWidth : 0;
|
||||
double leftEdge = rowGroupSpacerWidth;
|
||||
double width;
|
||||
if (this.OwningGrid.AreRowDetailsFrozen)
|
||||
{
|
||||
leftEdge += this.OwningGrid.HorizontalOffset;
|
||||
width = this.OwningGrid.CellsWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
xClip += this.OwningGrid.HorizontalOffset;
|
||||
width = Math.Max(this.OwningGrid.CellsWidth, this.OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth);
|
||||
}
|
||||
|
||||
// Details should not extend through the indented area
|
||||
width -= rowGroupSpacerWidth;
|
||||
double height = Math.Max(0, double.IsNaN(this.ContentHeight) ? 0 : this.ContentHeight);
|
||||
|
||||
foreach (UIElement child in this.Children)
|
||||
{
|
||||
child.Arrange(new Rect(leftEdge, 0, width, height));
|
||||
}
|
||||
|
||||
if (this.OwningGrid.AreRowDetailsFrozen)
|
||||
{
|
||||
// Frozen Details should not be clipped, similar to frozen cells
|
||||
this.Clip = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clip so Details doesn't obstruct elements to the left (the RowHeader by default) as we scroll to the right
|
||||
RectangleGeometry rg = new RectangleGeometry();
|
||||
rg.Rect = new Rect(xClip, 0, Math.Max(0, width - xClip + rowGroupSpacerWidth), height);
|
||||
this.Clip = rg;
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures the children of a <see cref="T:System.Windows.Controls.Primitives.DataGridDetailsPresenter"/> to
|
||||
/// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)"/> pass.
|
||||
/// </summary>
|
||||
/// <param name="availableSize">
|
||||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The size that the <see cref="T:System.Windows.Controls.Primitives.DataGridDetailsPresenter"/> determines it needs during layout, based on its calculations of child object allocated sizes.
|
||||
/// </returns>
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (this.OwningGrid == null || this.Children.Count == 0)
|
||||
{
|
||||
return new Size(0.0, 0.0);
|
||||
}
|
||||
|
||||
double desiredWidth = this.OwningGrid.AreRowDetailsFrozen ?
|
||||
this.OwningGrid.CellsWidth :
|
||||
Math.Max(this.OwningGrid.CellsWidth, this.OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth);
|
||||
|
||||
desiredWidth -= this.OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value;
|
||||
|
||||
foreach (UIElement child in this.Children)
|
||||
{
|
||||
child.Measure(new Size(desiredWidth, double.PositiveInfinity));
|
||||
}
|
||||
|
||||
double desiredHeight = Math.Max(0, double.IsNaN(this.ContentHeight) ? 0 : this.ContentHeight);
|
||||
|
||||
return new Size(desiredWidth, desiredHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
|
||||
/// </summary>
|
||||
/// <returns>An automation peer for this <see cref="T:System.Windows.Controls.Primitives.DataGridDetailsPresenter"/>.</returns>
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new DataGridDetailsPresenterAutomationPeer(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,391 @@
|
|||
// 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.Diagnostics;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals
|
||||
{
|
||||
internal class DataGridDisplayData
|
||||
{
|
||||
private Stack<DataGridRow> _fullyRecycledRows; // list of Rows that have been fully recycled (Collapsed)
|
||||
private int _headScrollingElements; // index of the row in _scrollingRows that is the first displayed row
|
||||
private DataGrid _owner;
|
||||
private Stack<DataGridRow> _recyclableRows; // list of Rows which have not been fully recycled (avoids Measure in several cases)
|
||||
private List<UIElement> _scrollingElements; // circular list of displayed elements
|
||||
private Stack<DataGridRowGroupHeader> _fullyRecycledGroupHeaders; // list of GroupHeaders that have been fully recycled (Collapsed)
|
||||
private Stack<DataGridRowGroupHeader> _recyclableGroupHeaders; // list of GroupHeaders which have not been fully recycled (avoids Measure in several cases)
|
||||
|
||||
public DataGridDisplayData(DataGrid owner)
|
||||
{
|
||||
_owner = owner;
|
||||
|
||||
ResetSlotIndexes();
|
||||
this.FirstDisplayedScrollingCol = -1;
|
||||
this.LastTotallyDisplayedScrollingCol = -1;
|
||||
|
||||
_scrollingElements = new List<UIElement>();
|
||||
_recyclableRows = new Stack<DataGridRow>();
|
||||
_fullyRecycledRows = new Stack<DataGridRow>();
|
||||
_recyclableGroupHeaders = new Stack<DataGridRowGroupHeader>();
|
||||
_fullyRecycledGroupHeaders = new Stack<DataGridRowGroupHeader>();
|
||||
}
|
||||
|
||||
public int FirstDisplayedScrollingCol
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int FirstScrollingSlot
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int LastScrollingSlot
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int LastTotallyDisplayedScrollingCol
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int NumDisplayedScrollingElements
|
||||
{
|
||||
get
|
||||
{
|
||||
return _scrollingElements.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public int NumTotallyDisplayedScrollingElements
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal double PendingVerticalScrollHeight
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal void AddRecylableRow(DataGridRow row)
|
||||
{
|
||||
Debug.Assert(!_recyclableRows.Contains(row), "Expected row parameter to be non-recyclable.");
|
||||
|
||||
row.DetachFromDataGrid(true);
|
||||
_recyclableRows.Push(row);
|
||||
}
|
||||
|
||||
internal void AddRecylableRowGroupHeader(DataGridRowGroupHeader groupHeader)
|
||||
{
|
||||
Debug.Assert(!_recyclableGroupHeaders.Contains(groupHeader), "Expected groupHeader parameter to be non-recyclable.");
|
||||
|
||||
groupHeader.PropertyName = null;
|
||||
groupHeader.PropertyValue = null;
|
||||
groupHeader.IsRecycled = true;
|
||||
_recyclableGroupHeaders.Push(groupHeader);
|
||||
}
|
||||
|
||||
internal void ClearElements(bool recycle)
|
||||
{
|
||||
ResetSlotIndexes();
|
||||
if (recycle)
|
||||
{
|
||||
foreach (UIElement element in _scrollingElements)
|
||||
{
|
||||
DataGridRow row = element as DataGridRow;
|
||||
if (row != null)
|
||||
{
|
||||
if (row.IsRecyclable)
|
||||
{
|
||||
AddRecylableRow(row);
|
||||
}
|
||||
else
|
||||
{
|
||||
row.Clip = new RectangleGeometry();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = element as DataGridRowGroupHeader;
|
||||
if (groupHeader != null)
|
||||
{
|
||||
AddRecylableRowGroupHeader(groupHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_recyclableRows.Clear();
|
||||
_fullyRecycledRows.Clear();
|
||||
_recyclableGroupHeaders.Clear();
|
||||
_fullyRecycledGroupHeaders.Clear();
|
||||
}
|
||||
|
||||
_scrollingElements.Clear();
|
||||
}
|
||||
|
||||
internal void CorrectSlotsAfterDeletion(int slot, bool wasCollapsed)
|
||||
{
|
||||
if (wasCollapsed)
|
||||
{
|
||||
if (slot > this.FirstScrollingSlot)
|
||||
{
|
||||
this.LastScrollingSlot--;
|
||||
}
|
||||
}
|
||||
else if (_owner.IsSlotVisible(slot))
|
||||
{
|
||||
UnloadScrollingElement(slot, true /*updateSlotInformation*/, true /*wasDeleted*/);
|
||||
}
|
||||
|
||||
// This cannot be an else condition because if there are 2 rows left, and you delete the first one
|
||||
// then these indexes need to be updated as well
|
||||
if (slot < this.FirstScrollingSlot)
|
||||
{
|
||||
this.FirstScrollingSlot--;
|
||||
this.LastScrollingSlot--;
|
||||
}
|
||||
}
|
||||
|
||||
internal void CorrectSlotsAfterInsertion(int slot, UIElement element, bool isCollapsed)
|
||||
{
|
||||
if (slot < this.FirstScrollingSlot)
|
||||
{
|
||||
// The row was inserted above our viewport, just update our indexes
|
||||
this.FirstScrollingSlot++;
|
||||
this.LastScrollingSlot++;
|
||||
}
|
||||
else if (isCollapsed && (slot <= this.LastScrollingSlot))
|
||||
{
|
||||
this.LastScrollingSlot++;
|
||||
}
|
||||
else if ((_owner.GetPreviousVisibleSlot(slot) <= this.LastScrollingSlot) || (this.LastScrollingSlot == -1))
|
||||
{
|
||||
Debug.Assert(element != null, "Expected non-null element.");
|
||||
|
||||
// The row was inserted in our viewport, add it as a scrolling row
|
||||
LoadScrollingSlot(slot, element, true /*updateSlotInformation*/);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetCircularListIndex(int slot, bool wrap)
|
||||
{
|
||||
int index = slot - this.FirstScrollingSlot - _headScrollingElements - _owner.GetCollapsedSlotCount(this.FirstScrollingSlot, slot);
|
||||
return wrap ? index % _scrollingElements.Count : index;
|
||||
}
|
||||
|
||||
internal void FullyRecycleElements()
|
||||
{
|
||||
// Fully recycle Recycleable rows and transfer them to Recycled rows
|
||||
while (_recyclableRows.Count > 0)
|
||||
{
|
||||
DataGridRow row = _recyclableRows.Pop();
|
||||
Debug.Assert(row != null, "Expected non-null row.");
|
||||
row.Visibility = Visibility.Collapsed;
|
||||
Debug.Assert(!_fullyRecycledRows.Contains(row), "Expected row not in _fullyRecycledRows.");
|
||||
_fullyRecycledRows.Push(row);
|
||||
}
|
||||
|
||||
// Fully recycle recyclable GroupHeaders and transfer them to Recycled GroupHeaders
|
||||
while (_recyclableGroupHeaders.Count > 0)
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = _recyclableGroupHeaders.Pop();
|
||||
Debug.Assert(groupHeader != null, "Expected non-null groupHeader.");
|
||||
groupHeader.Visibility = Visibility.Collapsed;
|
||||
Debug.Assert(!_fullyRecycledGroupHeaders.Contains(groupHeader), "Expected groupHeader not in _fullyRecycledGroupHeaders.");
|
||||
_fullyRecycledGroupHeaders.Push(groupHeader);
|
||||
}
|
||||
}
|
||||
|
||||
internal UIElement GetDisplayedElement(int slot)
|
||||
{
|
||||
Debug.Assert(slot >= this.FirstScrollingSlot, "Expected slot greater than or equal to FirstScrollingSlot.");
|
||||
Debug.Assert(slot <= this.LastScrollingSlot, "Expected slot less than or equal to LastScrollingSlot.");
|
||||
|
||||
return _scrollingElements[GetCircularListIndex(slot, true /*wrap*/)];
|
||||
}
|
||||
|
||||
internal DataGridRow GetDisplayedRow(int rowIndex)
|
||||
{
|
||||
return GetDisplayedElement(_owner.SlotFromRowIndex(rowIndex)) as DataGridRow;
|
||||
}
|
||||
|
||||
// Returns an enumeration of the displayed scrolling rows in order starting with the FirstDisplayedScrollingRow
|
||||
// Only DataGridRow instances are returned when onlyRows = true
|
||||
internal IEnumerable<UIElement> GetScrollingElements(bool onlyRows = false)
|
||||
{
|
||||
for (int i = 0; i < _scrollingElements.Count; i++)
|
||||
{
|
||||
UIElement element = _scrollingElements[(_headScrollingElements + i) % _scrollingElements.Count];
|
||||
if (!onlyRows || element is DataGridRow)
|
||||
{
|
||||
// _scrollingRows is a circular list that wraps
|
||||
yield return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridRowGroupHeader GetUsedGroupHeader()
|
||||
{
|
||||
if (_recyclableGroupHeaders.Count > 0)
|
||||
{
|
||||
return _recyclableGroupHeaders.Pop();
|
||||
}
|
||||
else if (_fullyRecycledGroupHeaders.Count > 0)
|
||||
{
|
||||
// For fully recycled rows, we need to set the Visibility back to Visible
|
||||
DataGridRowGroupHeader groupHeader = _fullyRecycledGroupHeaders.Pop();
|
||||
groupHeader.Visibility = Visibility.Visible;
|
||||
return groupHeader;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal DataGridRow GetUsedRow()
|
||||
{
|
||||
if (_recyclableRows.Count > 0)
|
||||
{
|
||||
return _recyclableRows.Pop();
|
||||
}
|
||||
else if (_fullyRecycledRows.Count > 0)
|
||||
{
|
||||
// For fully recycled rows, we need to set the Visibility back to Visible
|
||||
DataGridRow row = _fullyRecycledRows.Pop();
|
||||
row.Visibility = Visibility.Visible;
|
||||
return row;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Tracks the row at index rowIndex as a scrolling row
|
||||
internal void LoadScrollingSlot(int slot, UIElement element, bool updateSlotInformation)
|
||||
{
|
||||
if (_scrollingElements.Count == 0)
|
||||
{
|
||||
SetScrollingSlots(slot);
|
||||
_scrollingElements.Add(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The slot should be adjacent to the other slots being displayed
|
||||
Debug.Assert(slot >= _owner.GetPreviousVisibleSlot(this.FirstScrollingSlot), "Expected slot greater than or equal to _owner.GetPreviousVisibleSlot(this.FirstScrollingSlot).");
|
||||
Debug.Assert(slot <= _owner.GetNextVisibleSlot(this.LastScrollingSlot), "Expected slot smaller than or equal to _owner.GetNextVisibleSlot(this.LastScrollingSlot).");
|
||||
if (updateSlotInformation)
|
||||
{
|
||||
if (slot < this.FirstScrollingSlot)
|
||||
{
|
||||
this.FirstScrollingSlot = slot;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LastScrollingSlot = _owner.GetNextVisibleSlot(this.LastScrollingSlot);
|
||||
}
|
||||
}
|
||||
|
||||
int insertIndex = GetCircularListIndex(slot, false /*wrap*/);
|
||||
if (insertIndex > _scrollingElements.Count)
|
||||
{
|
||||
// We need to wrap around from the bottom to the top of our circular list; as a result the head of the list moves forward
|
||||
insertIndex -= _scrollingElements.Count;
|
||||
_headScrollingElements++;
|
||||
}
|
||||
|
||||
_scrollingElements.Insert(insertIndex, element);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetSlotIndexes()
|
||||
{
|
||||
SetScrollingSlots(-1);
|
||||
this.NumTotallyDisplayedScrollingElements = 0;
|
||||
_headScrollingElements = 0;
|
||||
}
|
||||
|
||||
private void SetScrollingSlots(int newValue)
|
||||
{
|
||||
this.FirstScrollingSlot = newValue;
|
||||
this.LastScrollingSlot = newValue;
|
||||
}
|
||||
|
||||
// Stops tracking the element at the given slot as a scrolling element
|
||||
internal void UnloadScrollingElement(int slot, bool updateSlotInformation, bool wasDeleted)
|
||||
{
|
||||
Debug.Assert(_owner.IsSlotVisible(slot), "Expected slot is visible.");
|
||||
|
||||
int elementIndex = GetCircularListIndex(slot, false /*wrap*/);
|
||||
if (elementIndex > _scrollingElements.Count)
|
||||
{
|
||||
// We need to wrap around from the top to the bottom of our circular list
|
||||
elementIndex -= _scrollingElements.Count;
|
||||
_headScrollingElements--;
|
||||
}
|
||||
|
||||
_scrollingElements.RemoveAt(elementIndex);
|
||||
|
||||
if (updateSlotInformation)
|
||||
{
|
||||
if (slot == this.FirstScrollingSlot && !wasDeleted)
|
||||
{
|
||||
this.FirstScrollingSlot = _owner.GetNextVisibleSlot(this.FirstScrollingSlot);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LastScrollingSlot = _owner.GetPreviousVisibleSlot(this.LastScrollingSlot);
|
||||
}
|
||||
|
||||
if (this.LastScrollingSlot < this.FirstScrollingSlot)
|
||||
{
|
||||
ResetSlotIndexes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
internal void PrintDisplay()
|
||||
{
|
||||
foreach (UIElement element in this.GetScrollingElements())
|
||||
{
|
||||
DataGridRow row = element as DataGridRow;
|
||||
if (row != null)
|
||||
{
|
||||
Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} Row: {1} ", row.Slot, row.Index));
|
||||
}
|
||||
else
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = element as DataGridRowGroupHeader;
|
||||
if (groupHeader != null)
|
||||
{
|
||||
#if FEATURE_ICOLLECTIONVIEW_GROUP
|
||||
Debug.WriteLine(string.Format(
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
"Slot: {0} GroupHeader: {1}",
|
||||
groupHeader.RowGroupInfo.Slot,
|
||||
groupHeader.RowGroupInfo.CollectionViewGroup.Name));
|
||||
#else
|
||||
Debug.WriteLine(string.Format(
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
"Slot: {0} GroupHeader: {1}",
|
||||
groupHeader.RowGroupInfo.Slot,
|
||||
groupHeader.RowGroupInfo.ToString()));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
// 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>
|
||||
/// Defines modes that indicates how DataGrid content is copied to the Clipboard.
|
||||
/// </summary>
|
||||
public enum DataGridClipboardCopyMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Disable the DataGrid's ability to copy selected items as text.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Enable the DataGrid's ability to copy selected items as text, but do not include
|
||||
/// the column header content as the first line in the text that gets copied to the Clipboard.
|
||||
/// </summary>
|
||||
ExcludeHeader,
|
||||
|
||||
/// <summary>
|
||||
/// Enable the DataGrid's ability to copy selected items as text, and include
|
||||
/// the column header content as the first line in the text that gets copied to the Clipboard.
|
||||
/// </summary>
|
||||
IncludeHeader
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to specify action to take out of edit mode.
|
||||
/// </summary>
|
||||
public enum DataGridEditAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Cancel the changes.
|
||||
/// </summary>
|
||||
Cancel,
|
||||
|
||||
/// <summary>
|
||||
/// Commit edited value.
|
||||
/// </summary>
|
||||
Commit
|
||||
}
|
||||
|
||||
// Determines the location and visibility of the editing row.
|
||||
internal enum DataGridEditingRowLocation
|
||||
{
|
||||
Bottom = 0, // The editing row is collapsed below the displayed rows
|
||||
Inline = 1, // The editing row is visible and displayed
|
||||
Top = 2 // The editing row is collapsed above the displayed rows
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the inner cells' vertical/horizontal gridlines are shown or not.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DataGridGridLinesVisibility
|
||||
{
|
||||
/// <summary>
|
||||
/// None DataGridGridLinesVisibility
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal DataGridGridLinesVisibility
|
||||
/// </summary>
|
||||
Horizontal = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Vertical DataGridGridLinesVisibility
|
||||
/// </summary>
|
||||
Vertical = 2,
|
||||
|
||||
/// <summary>
|
||||
/// All DataGridGridLinesVisibility
|
||||
/// </summary>
|
||||
All = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the current cell or row is edited.
|
||||
/// </summary>
|
||||
public enum DataGridEditingUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// Cell DataGridEditingUnit
|
||||
/// </summary>
|
||||
Cell = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Row DataGridEditingUnit
|
||||
/// </summary>
|
||||
Row = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the row/column headers are shown or not.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DataGridHeadersVisibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Show Row, Column, and Corner Headers
|
||||
/// </summary>
|
||||
All = Row | Column,
|
||||
|
||||
/// <summary>
|
||||
/// Show only Column Headers with top-right corner Header
|
||||
/// </summary>
|
||||
Column = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Show only Row Headers with bottom-left corner
|
||||
/// </summary>
|
||||
Row = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Don’t show any Headers
|
||||
/// </summary>
|
||||
None = 0x00
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the visibility of the row details.
|
||||
/// </summary>
|
||||
public enum DataGridRowDetailsVisibilityMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Collapsed DataGridRowDetailsVisibilityMode
|
||||
/// </summary>
|
||||
Collapsed = 2, // Show no details. Developer is in charge of toggling visibility.
|
||||
|
||||
/// <summary>
|
||||
/// Visible DataGridRowDetailsVisibilityMode
|
||||
/// </summary>
|
||||
Visible = 1, // Show the details section for all rows.
|
||||
|
||||
/// <summary>
|
||||
/// VisibleWhenSelected DataGridRowDetailsVisibilityMode
|
||||
/// </summary>
|
||||
VisibleWhenSelected = 0 // Show the details section only for the selected row(s).
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the type of action to take when selecting items.
|
||||
/// </summary>
|
||||
internal enum DataGridSelectionAction
|
||||
{
|
||||
AddCurrentToSelection,
|
||||
None,
|
||||
RemoveCurrentFromSelection,
|
||||
SelectCurrent,
|
||||
SelectFromAnchorToCurrent
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the selection model.
|
||||
/// </summary>
|
||||
public enum DataGridSelectionMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Extended DataGridSelectionMode
|
||||
/// </summary>
|
||||
Extended = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Single DataGridSelectionMode
|
||||
/// </summary>
|
||||
Single = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the sort direction of a column.
|
||||
/// </summary>
|
||||
public enum DataGridSortDirection
|
||||
{
|
||||
/// <summary>
|
||||
/// Sorts in ascending order.
|
||||
/// </summary>
|
||||
Ascending = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Sorts in descending order.
|
||||
/// </summary>
|
||||
Descending = 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
// 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.Globalization;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals
|
||||
{
|
||||
internal static class DataGridError
|
||||
{
|
||||
public static class DataGrid
|
||||
{
|
||||
public static InvalidOperationException CannotChangeItemsWhenLoadingRows()
|
||||
{
|
||||
return new InvalidOperationException("Items cannot be added, removed or reset while rows are loading or unloading.");
|
||||
}
|
||||
|
||||
public static InvalidOperationException CannotChangeColumnCollectionWhileAdjustingDisplayIndexes()
|
||||
{
|
||||
return new InvalidOperationException("Column collection cannot be changed while adjusting display indexes.");
|
||||
}
|
||||
|
||||
public static InvalidOperationException ColumnCannotBeCollapsed()
|
||||
{
|
||||
return new InvalidOperationException("Column cannot be collapsed.");
|
||||
}
|
||||
|
||||
public static InvalidOperationException ColumnCannotBeReassignedToDifferentDataGrid()
|
||||
{
|
||||
return new InvalidOperationException("Column already belongs to a DataGrid instance and cannot be reassigned.");
|
||||
}
|
||||
|
||||
public static ArgumentException ColumnNotInThisDataGrid()
|
||||
{
|
||||
return new ArgumentException("Provided column does not belong to this DataGrid.");
|
||||
}
|
||||
|
||||
public static ArgumentException ItemIsNotContainedInTheItemsSource(string paramName)
|
||||
{
|
||||
return new ArgumentException("The item is not contained in the ItemsSource.", paramName);
|
||||
}
|
||||
|
||||
public static InvalidOperationException NoCurrentRow()
|
||||
{
|
||||
return new InvalidOperationException("There is no current row. Operation cannot be completed.");
|
||||
}
|
||||
|
||||
public static InvalidOperationException NoOwningGrid(Type type)
|
||||
{
|
||||
return new InvalidOperationException(Format("There is no instance of DataGrid assigned to this {0}. Operation cannot be completed.", type.FullName));
|
||||
}
|
||||
|
||||
public static InvalidOperationException UnderlyingPropertyIsReadOnly(string paramName)
|
||||
{
|
||||
return new InvalidOperationException(Format("{0} cannot be set because the underlying property is read only.", paramName));
|
||||
}
|
||||
|
||||
public static ArgumentException ValueCannotBeSetToInfinity(string paramName)
|
||||
{
|
||||
return new ArgumentException(Format("{0} cannot be set to infinity.", paramName));
|
||||
}
|
||||
|
||||
public static ArgumentException ValueCannotBeSetToNAN(string paramName)
|
||||
{
|
||||
return new ArgumentException(Format("{0} cannot be set to double.NAN.", paramName));
|
||||
}
|
||||
|
||||
public static ArgumentNullException ValueCannotBeSetToNull(string paramName, string valueName)
|
||||
{
|
||||
return new ArgumentNullException(paramName, Format("{0} cannot be set to a null value.", valueName));
|
||||
}
|
||||
|
||||
public static ArgumentException ValueIsNotAnInstanceOf(string paramName, Type type)
|
||||
{
|
||||
return new ArgumentException(paramName, Format("The value is not an instance of {0}.", type.FullName));
|
||||
}
|
||||
|
||||
public static ArgumentException ValueIsNotAnInstanceOfEitherOr(string paramName, Type type1, Type type2)
|
||||
{
|
||||
return new ArgumentException(paramName, Format("The value is not an instance of {0} or {1}.", type1.FullName, type2.FullName));
|
||||
}
|
||||
|
||||
public static ArgumentOutOfRangeException ValueMustBeBetween(string paramName, string valueName, object lowValue, bool lowInclusive, object highValue, bool highInclusive)
|
||||
{
|
||||
string message;
|
||||
|
||||
if (lowInclusive && highInclusive)
|
||||
{
|
||||
message = "{0} must be greater than or equal to {1} and less than or equal to {2}.";
|
||||
}
|
||||
else if (lowInclusive && !highInclusive)
|
||||
{
|
||||
message = "{0} must be greater than or equal to {1} and less than {2}.";
|
||||
}
|
||||
else if (!lowInclusive && highInclusive)
|
||||
{
|
||||
message = "{0} must be greater than {1} and less than or equal to {2}.";
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "{0} must be greater than {1} and less than {2}.";
|
||||
}
|
||||
|
||||
return new ArgumentOutOfRangeException(paramName, Format(message, valueName, lowValue, highValue));
|
||||
}
|
||||
|
||||
public static ArgumentOutOfRangeException ValueMustBeGreaterThanOrEqualTo(string paramName, string valueName, object value)
|
||||
{
|
||||
return new ArgumentOutOfRangeException(paramName, Format("{0} must be greater than or equal to {1}.", valueName, value));
|
||||
}
|
||||
|
||||
public static ArgumentOutOfRangeException ValueMustBeLessThanOrEqualTo(string paramName, string valueName, object value)
|
||||
{
|
||||
return new ArgumentOutOfRangeException(paramName, Format("{0} must be less than or equal to {1}.", valueName, value));
|
||||
}
|
||||
|
||||
public static ArgumentOutOfRangeException ValueMustBeLessThan(string paramName, string valueName, object value)
|
||||
{
|
||||
return new ArgumentOutOfRangeException(paramName, Format("{0} must be less than {1}.", valueName, value));
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridAutomationPeer
|
||||
{
|
||||
public static InvalidOperationException OperationCannotBePerformed()
|
||||
{
|
||||
return new InvalidOperationException("Cannot perform the operation.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridColumnHeader
|
||||
{
|
||||
public static NotSupportedException ContentDoesNotSupportUIElements()
|
||||
{
|
||||
return new NotSupportedException("Content does not support UIElement; use ContentTemplate instead.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridLength
|
||||
{
|
||||
public static ArgumentException InvalidUnitType(string paramName)
|
||||
{
|
||||
return new ArgumentException(Format("{0} is not a valid DataGridLengthUnitType.", paramName), paramName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridLengthConverter
|
||||
{
|
||||
public static NotSupportedException CannotConvertFrom(string paramName)
|
||||
{
|
||||
return new NotSupportedException(Format("DataGridLengthConverter cannot convert from {0}.", paramName));
|
||||
}
|
||||
|
||||
public static NotSupportedException CannotConvertTo(string paramName)
|
||||
{
|
||||
return new NotSupportedException(Format("Cannot convert from DataGridLength to {0}.", paramName));
|
||||
}
|
||||
|
||||
public static NotSupportedException InvalidDataGridLength(string paramName)
|
||||
{
|
||||
return new NotSupportedException(Format("Invalid DataGridLength.", paramName));
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridRow
|
||||
{
|
||||
public static InvalidOperationException InvalidRowIndexCannotCompleteOperation()
|
||||
{
|
||||
return new InvalidOperationException("Invalid row index. Operation cannot be completed.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridSelectedItemsCollection
|
||||
{
|
||||
public static InvalidOperationException CannotChangeSelectedItemsCollectionInSingleMode()
|
||||
{
|
||||
return new InvalidOperationException("Can only change SelectedItems collection in Extended selection mode. Use SelectedItem property in Single selection mode.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridComboBoxColumn
|
||||
{
|
||||
public static ArgumentException UnsetBinding(string header)
|
||||
{
|
||||
return new ArgumentException(Format("Binding for column {0} is null. Ensure that the binding path has been set correctly.", header));
|
||||
}
|
||||
|
||||
public static ArgumentException UnsetBinding(Type type)
|
||||
{
|
||||
return new ArgumentException(Format("Binding for column of type {0} is null. Ensure that the binding path has been set correctly.", type.FullName));
|
||||
}
|
||||
|
||||
public static ArgumentException UnknownBindingPath(Binding binding, Type type)
|
||||
{
|
||||
return new ArgumentException(Format("Binding path {0} could not be found in type {1}. Ensure that the binding path has been set correctly.", binding.Path.Path, type.FullName));
|
||||
}
|
||||
|
||||
public static ArgumentException UnknownDisplayMemberPath(string displayMemberPath, Type type)
|
||||
{
|
||||
return new ArgumentException(Format("DisplayMemberPath {0} could not be found in type {1}. Ensure that the value has been set correctly and note that for built-in types DisplayMemberPath should not be used.", displayMemberPath, type.FullName));
|
||||
}
|
||||
|
||||
public static ArgumentException UnknownItemsSourcePath(Binding binding)
|
||||
{
|
||||
return new ArgumentException(Format("The ItemsSource elements do not contain a property {0}. Ensure that the binding path has been set correctly.", binding.Path.Path));
|
||||
}
|
||||
|
||||
public static ArgumentException BindingTypeMismatch(Type bindingType, Type itemSourceType)
|
||||
{
|
||||
return new ArgumentException(Format("The DataGridComboBoxColumn ItemSource elements of type \'{0}\' do not match the Binding type \'{1}\'. Ensure that the paths have been set correctly and specify a DisplayMemberPath for non built-in types.", itemSourceType.FullName, bindingType.FullName));
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGridTemplateColumn
|
||||
{
|
||||
public static TypeInitializationException MissingTemplateForType(Type type)
|
||||
{
|
||||
return new TypeInitializationException(Format("Missing template. Cannot initialize {0}.", type.FullName), null);
|
||||
}
|
||||
}
|
||||
|
||||
private static string Format(string formatString, params object[] args)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, formatString, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.Primitives;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
internal class DataGridFillerColumn : DataGridColumn
|
||||
{
|
||||
public DataGridFillerColumn(DataGrid owningGrid)
|
||||
{
|
||||
this.IsReadOnly = true;
|
||||
this.OwningGrid = owningGrid;
|
||||
this.MinWidth = 0;
|
||||
this.MaxWidth = int.MaxValue;
|
||||
}
|
||||
|
||||
internal double FillerWidth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
// True if there is room for the filler column; otherwise, false
|
||||
internal bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.FillerWidth > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// True if the FillerColumn's header cell is contained in the visual tree
|
||||
internal bool IsRepresented
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal override DataGridColumnHeader CreateHeader()
|
||||
{
|
||||
DataGridColumnHeader headerCell = base.CreateHeader();
|
||||
if (headerCell != null)
|
||||
{
|
||||
Windows.UI.Xaml.Automation.AutomationProperties.SetAccessibilityView(
|
||||
headerCell,
|
||||
Windows.UI.Xaml.Automation.Peers.AccessibilityView.Raw);
|
||||
headerCell.IsEnabled = false;
|
||||
}
|
||||
|
||||
return headerCell;
|
||||
}
|
||||
|
||||
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
|
||||
{
|
||||
Debug.Assert(false, "Unexpected call to DataGridFillerColumn.PrepareCellForEdit.");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a non-scrollable grid that contains <see cref="DataGrid"/> row headers.
|
||||
/// </summary>
|
||||
public class DataGridFrozenGrid : Grid
|
||||
{
|
||||
/// <summary>
|
||||
/// A dependency property that indicates whether the grid is frozen.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsFrozenProperty = DependencyProperty.RegisterAttached(
|
||||
"IsFrozen",
|
||||
typeof(bool),
|
||||
typeof(DataGridFrozenGrid),
|
||||
null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether the grid is frozen.
|
||||
/// </summary>
|
||||
/// <param name="element">
|
||||
/// The object to get the IsFrozen value from.
|
||||
/// </param>
|
||||
/// <returns>true if the grid is frozen; otherwise, false. The default is true.</returns>
|
||||
public static bool GetIsFrozen(DependencyObject element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException("element");
|
||||
}
|
||||
|
||||
return (bool)element.GetValue(IsFrozenProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a value that indicates whether the grid is frozen.
|
||||
/// </summary>
|
||||
/// <param name="element">The object to set the IsFrozen value on.</param>
|
||||
/// <param name="value">true if <paramref name="element"/> is frozen; otherwise, false.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException"><paramref name="element"/> is null.</exception>
|
||||
public static void SetIsFrozen(DependencyObject element, bool value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException("element");
|
||||
}
|
||||
|
||||
element.SetValue(IsFrozenProperty, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// 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.Input;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals
|
||||
{
|
||||
internal class DataGridInteractionInfo
|
||||
{
|
||||
internal uint CapturedPointerId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal bool IsPointerOver
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,477 @@
|
|||
// 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.Globalization;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Microsoft.Toolkit.Uwp.Utilities;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// DataGridLengthUnitType
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These aren't flags.
|
||||
/// </remarks>
|
||||
public enum DataGridLengthUnitType
|
||||
{
|
||||
/// <summary>
|
||||
/// Auto DataGridLengthUnitType
|
||||
/// </summary>
|
||||
Auto = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Pixel DataGridLengthUnitType
|
||||
/// </summary>
|
||||
Pixel = 1,
|
||||
|
||||
/// <summary>
|
||||
/// SizeToCells DataGridLengthUnitType
|
||||
/// </summary>
|
||||
SizeToCells = 2,
|
||||
|
||||
/// <summary>
|
||||
/// SizeToHeader DataGridLengthUnitType
|
||||
/// </summary>
|
||||
SizeToHeader = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Star DataGridLengthUnitType
|
||||
/// </summary>
|
||||
Star = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the lengths of elements within the <see cref="DataGrid"/> control.
|
||||
/// </summary>
|
||||
[Windows.Foundation.Metadata.CreateFromString(MethodName = "Microsoft.Toolkit.Uwp.UI.Controls.DataGridLength.ConvertFromString")]
|
||||
public struct DataGridLength : IEquatable<DataGridLength>
|
||||
{
|
||||
// static instances of value invariant DataGridLengths
|
||||
private static readonly DataGridLength _auto = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.Auto);
|
||||
private static readonly DataGridLength _sizeToCells = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToCells);
|
||||
private static readonly DataGridLength _sizeToHeader = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToHeader);
|
||||
|
||||
private static string _starSuffix = "*";
|
||||
private static string[] _valueInvariantUnitStrings = { "auto", "sizetocells", "sizetoheader" };
|
||||
private static DataGridLength[] _valueInvariantDataGridLengths = { DataGridLength.Auto, DataGridLength.SizeToCells, DataGridLength.SizeToHeader };
|
||||
|
||||
private double _desiredValue; // desired value storage
|
||||
private double _displayValue; // display value storage
|
||||
private double _unitValue; // unit value storage
|
||||
private DataGridLengthUnitType _unitType; // unit type storage
|
||||
|
||||
internal const double DATAGRIDLENGTH_DefaultValue = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridLength"/> struct based on a numerical value.
|
||||
/// </summary>
|
||||
/// <param name="value">numerical length</param>
|
||||
public DataGridLength(double value)
|
||||
: this(value, DataGridLengthUnitType.Pixel)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridLength"/> struct based on a numerical value and a type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to hold.</param>
|
||||
/// <param name="type">The unit of <c>value</c>.</param>
|
||||
/// <remarks>
|
||||
/// <c>value</c> is ignored unless <c>type</c> is
|
||||
/// <c>DataGridLengthUnitType.Pixel</c> or
|
||||
/// <c>DataGridLengthUnitType.Star</c>
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// If <c>value</c> parameter is <c>double.NaN</c>
|
||||
/// or <c>value</c> parameter is <c>double.NegativeInfinity</c>
|
||||
/// or <c>value</c> parameter is <c>double.PositiveInfinity</c>.
|
||||
/// </exception>
|
||||
public DataGridLength(double value, DataGridLengthUnitType type)
|
||||
: this(value, type, type == DataGridLengthUnitType.Pixel ? value : double.NaN, type == DataGridLengthUnitType.Pixel ? value : double.NaN)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridLength"/> struct based on a numerical value and a unit.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to hold.</param>
|
||||
/// <param name="type">The unit of <c>value</c>.</param>
|
||||
/// <param name="desiredValue">The desired value.</param>
|
||||
/// <param name="displayValue">The display value.</param>
|
||||
/// <remarks>
|
||||
/// <c>value</c> is ignored unless <c>type</c> is
|
||||
/// <c>DataGridLengthUnitType.Pixel</c> or
|
||||
/// <c>DataGridLengthUnitType.Star</c>
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// If <c>value</c> parameter is <c>double.NaN</c>
|
||||
/// or <c>value</c> parameter is <c>double.NegativeInfinity</c>
|
||||
/// or <c>value</c> parameter is <c>double.PositiveInfinity</c>.
|
||||
/// </exception>
|
||||
public DataGridLength(double value, DataGridLengthUnitType type, double desiredValue, double displayValue)
|
||||
{
|
||||
if (double.IsNaN(value))
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueCannotBeSetToNAN("value");
|
||||
}
|
||||
|
||||
if (double.IsInfinity(value))
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("value");
|
||||
}
|
||||
|
||||
if (double.IsInfinity(desiredValue))
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("desiredValue");
|
||||
}
|
||||
|
||||
if (double.IsInfinity(displayValue))
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("displayValue");
|
||||
}
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "value", 0);
|
||||
}
|
||||
|
||||
if (desiredValue < 0)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("desiredValue", "desiredValue", 0);
|
||||
}
|
||||
|
||||
if (displayValue < 0)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("displayValue", "displayValue", 0);
|
||||
}
|
||||
|
||||
if (type != DataGridLengthUnitType.Auto &&
|
||||
type != DataGridLengthUnitType.SizeToCells &&
|
||||
type != DataGridLengthUnitType.SizeToHeader &&
|
||||
type != DataGridLengthUnitType.Star &&
|
||||
type != DataGridLengthUnitType.Pixel)
|
||||
{
|
||||
throw DataGridError.DataGridLength.InvalidUnitType("type");
|
||||
}
|
||||
|
||||
_desiredValue = desiredValue;
|
||||
_displayValue = displayValue;
|
||||
_unitValue = (type == DataGridLengthUnitType.Auto) ? DATAGRIDLENGTH_DefaultValue : value;
|
||||
_unitType = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="DataGridLength"/> structure that represents the standard automatic sizing mode.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="DataGridLength"/> structure that represents the standard automatic sizing mode.
|
||||
/// </returns>
|
||||
public static DataGridLength Auto
|
||||
{
|
||||
get
|
||||
{
|
||||
return _auto;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="DataGridLength"/> structure that represents the cell-based automatic sizing mode.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="DataGridLength"/> structure that represents the cell-based automatic sizing mode.
|
||||
/// </returns>
|
||||
public static DataGridLength SizeToCells
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sizeToCells;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="DataGridLength"/> structure that represents the header-based automatic sizing mode.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="DataGridLength"/> structure that represents the header-based automatic sizing mode.
|
||||
/// </returns>
|
||||
public static DataGridLength SizeToHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sizeToHeader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the desired value of this instance.
|
||||
/// </summary>
|
||||
public double DesiredValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return _desiredValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display value of this instance.
|
||||
/// </summary>
|
||||
public double DisplayValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return _displayValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this DataGridLength instance holds an absolute (pixel) value.
|
||||
/// </summary>
|
||||
public bool IsAbsolute
|
||||
{
|
||||
get
|
||||
{
|
||||
return _unitType == DataGridLengthUnitType.Pixel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this DataGridLength instance is automatic (not specified).
|
||||
/// </summary>
|
||||
public bool IsAuto
|
||||
{
|
||||
get
|
||||
{
|
||||
return _unitType == DataGridLengthUnitType.Auto;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this DataGridLength instance is to size to the cells of a column or row.
|
||||
/// </summary>
|
||||
public bool IsSizeToCells
|
||||
{
|
||||
get
|
||||
{
|
||||
return _unitType == DataGridLengthUnitType.SizeToCells;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this DataGridLength instance is to size to the header of a column or row.
|
||||
/// </summary>
|
||||
public bool IsSizeToHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return _unitType == DataGridLengthUnitType.SizeToHeader;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this DataGridLength instance holds a weighted proportion of available space.
|
||||
/// </summary>
|
||||
public bool IsStar
|
||||
{
|
||||
get
|
||||
{
|
||||
return _unitType == DataGridLengthUnitType.Star;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="DataGridLengthUnitType"/> that represents the current sizing mode.
|
||||
/// </summary>
|
||||
public DataGridLengthUnitType UnitType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _unitType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute value of the <see cref="DataGridLength"/> in pixels.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The absolute value of the <see cref="DataGridLength"/> in pixels.
|
||||
/// </returns>
|
||||
public double Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return _unitValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string into a <see cref="DataGridLength"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="value">string to convert.</param>
|
||||
/// <returns>The result of the conversion.</returns>
|
||||
public static DataGridLength ConvertFromString(string value)
|
||||
{
|
||||
return ConvertFrom(null, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an object into a <see cref="DataGridLength"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="culture">optional culture to use for conversion.</param>
|
||||
/// <param name="value">object to convert.</param>
|
||||
/// <returns>The result of the conversion.</returns>
|
||||
public static DataGridLength ConvertFrom(CultureInfo culture, object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw DataGridError.DataGridLengthConverter.CannotConvertFrom("(null)");
|
||||
}
|
||||
|
||||
string stringValue = value as string;
|
||||
if (stringValue != null)
|
||||
{
|
||||
stringValue = stringValue.Trim();
|
||||
|
||||
if (stringValue.EndsWith(_starSuffix, StringComparison.Ordinal))
|
||||
{
|
||||
string stringValueWithoutSuffix = stringValue.Substring(0, stringValue.Length - _starSuffix.Length);
|
||||
|
||||
double starWeight;
|
||||
if (string.IsNullOrEmpty(stringValueWithoutSuffix))
|
||||
{
|
||||
starWeight = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
starWeight = Convert.ToDouble(stringValueWithoutSuffix, culture ?? CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
return new DataGridLength(starWeight, DataGridLengthUnitType.Star);
|
||||
}
|
||||
|
||||
for (int index = 0; index < _valueInvariantUnitStrings.Length; index++)
|
||||
{
|
||||
if (stringValue.Equals(_valueInvariantUnitStrings[index], StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return _valueInvariantDataGridLengths[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion from numeric type
|
||||
double doubleValue = Convert.ToDouble(value, culture ?? CultureInfo.CurrentCulture);
|
||||
if (double.IsNaN(doubleValue))
|
||||
{
|
||||
return DataGridLength.Auto;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new DataGridLength(doubleValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="DataGridLength"/> instance into a string.
|
||||
/// </summary>
|
||||
/// <param name="culture">optional culture to use for conversion.</param>
|
||||
/// <param name="value">value to convert.</param>
|
||||
/// <returns>The result of the conversion.</returns>
|
||||
public static string ConvertToString(CultureInfo culture, DataGridLength value)
|
||||
{
|
||||
// Convert dataGridLength to a string
|
||||
switch (value.UnitType)
|
||||
{
|
||||
// for Auto print out "Auto". value is always "1.0"
|
||||
case DataGridLengthUnitType.Auto:
|
||||
return "Auto";
|
||||
|
||||
case DataGridLengthUnitType.SizeToHeader:
|
||||
return "SizeToHeader";
|
||||
|
||||
case DataGridLengthUnitType.SizeToCells:
|
||||
return "SizeToCells";
|
||||
|
||||
// Star has one special case when value is "1.0".
|
||||
// in this case drop value part and print only "Star"
|
||||
case DataGridLengthUnitType.Star:
|
||||
return
|
||||
DoubleUtil.AreClose(1.0, value.Value)
|
||||
? _starSuffix
|
||||
: Convert.ToString(value.Value, culture ?? CultureInfo.CurrentCulture) + _starSuffix;
|
||||
|
||||
default:
|
||||
return Convert.ToString(value.Value, culture ?? CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overloaded operator, compares 2 DataGridLength's.
|
||||
/// </summary>
|
||||
/// <param name="gl1">first DataGridLength to compare.</param>
|
||||
/// <param name="gl2">second DataGridLength to compare.</param>
|
||||
/// <returns>true if specified DataGridLength have same value,
|
||||
/// unit type, desired value, and display value.</returns>
|
||||
public static bool operator ==(DataGridLength gl1, DataGridLength gl2)
|
||||
{
|
||||
return gl1.UnitType == gl2.UnitType &&
|
||||
gl1.Value == gl2.Value &&
|
||||
gl1.DesiredValue == gl2.DesiredValue &&
|
||||
gl1.DisplayValue == gl2.DisplayValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overloaded operator, compares 2 DataGridLength's.
|
||||
/// </summary>
|
||||
/// <param name="gl1">first DataGridLength to compare.</param>
|
||||
/// <param name="gl2">second DataGridLength to compare.</param>
|
||||
/// <returns>true if specified DataGridLength have either different value,
|
||||
/// unit type, desired value, or display value.</returns>
|
||||
public static bool operator !=(DataGridLength gl1, DataGridLength gl2)
|
||||
{
|
||||
return gl1.UnitType != gl2.UnitType ||
|
||||
gl1.Value != gl2.Value ||
|
||||
gl1.DesiredValue != gl2.DesiredValue ||
|
||||
gl1.DisplayValue != gl2.DisplayValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance of DataGridLength with another instance.
|
||||
/// </summary>
|
||||
/// <param name="other">DataGridLength length instance to compare.</param>
|
||||
/// <returns><c>true</c> if this DataGridLength instance has the same value
|
||||
/// and unit type as gridLength.</returns>
|
||||
public bool Equals(DataGridLength other)
|
||||
{
|
||||
return this == other;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance of DataGridLength with another object.
|
||||
/// </summary>
|
||||
/// <param name="obj">Reference to an object for comparison.</param>
|
||||
/// <returns><c>true</c> if this DataGridLength instance has the same value
|
||||
/// and unit type as oCompare.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
DataGridLength? dataGridLength = obj as DataGridLength?;
|
||||
if (dataGridLength.HasValue)
|
||||
{
|
||||
return this == dataGridLength;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a unique hash code for this DataGridLength
|
||||
/// </summary>
|
||||
/// <returns>hash code</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)_unitValue + (int)_unitType + (int)_desiredValue + (int)_displayValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data for the <see cref="E:Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.PreparingCellForEdit"/> event.
|
||||
/// </summary>
|
||||
public class DataGridPreparingCellForEditEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridPreparingCellForEditEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="column">The column that contains the cell to be edited.</param>
|
||||
/// <param name="row">The row that contains the cell to be edited.</param>
|
||||
/// <param name="editingEventArgs">Information about the user gesture that caused the cell to enter edit mode.</param>
|
||||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
||||
public DataGridPreparingCellForEditEventArgs(
|
||||
DataGridColumn column,
|
||||
DataGridRow row,
|
||||
RoutedEventArgs editingEventArgs,
|
||||
FrameworkElement editingElement)
|
||||
{
|
||||
this.Column = column;
|
||||
this.Row = row;
|
||||
this.EditingEventArgs = editingEventArgs;
|
||||
this.EditingElement = editingElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the column that contains the cell to be edited.
|
||||
/// </summary>
|
||||
public DataGridColumn Column
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element that the column displays for a cell in editing mode.
|
||||
/// </summary>
|
||||
public FrameworkElement EditingElement
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the user gesture that caused the cell to enter edit mode.
|
||||
/// </summary>
|
||||
public RoutedEventArgs EditingEventArgs
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row that contains the cell to be edited.
|
||||
/// </summary>
|
||||
public DataGridRow Row
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,68 @@
|
|||
// 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.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// This class encapsulates a selected row's information necessary for the CopyingRowClipboardContent event.
|
||||
/// </summary>
|
||||
public class DataGridRowClipboardEventArgs : EventArgs
|
||||
{
|
||||
private List<DataGridClipboardCellContent> _clipboardRowContent;
|
||||
private bool _isColumnHeadersRow;
|
||||
private object _item;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowClipboardEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="item">The row's associated data item.</param>
|
||||
/// <param name="isColumnHeadersRow">Whether or not this EventArgs is for the column headers.</param>
|
||||
internal DataGridRowClipboardEventArgs(object item, bool isColumnHeadersRow)
|
||||
{
|
||||
_isColumnHeadersRow = isColumnHeadersRow;
|
||||
_item = item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list used to modify, add or remove a cell content before it gets stored into the clipboard.
|
||||
/// </summary>
|
||||
public List<DataGridClipboardCellContent> ClipboardRowContent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_clipboardRowContent == null)
|
||||
{
|
||||
_clipboardRowContent = new List<DataGridClipboardCellContent>();
|
||||
}
|
||||
|
||||
return _clipboardRowContent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this property is true when the ClipboardRowContent represents column headers, in which case the Item is null.
|
||||
/// </summary>
|
||||
public bool IsColumnHeadersRow
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isColumnHeadersRow;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="DataGrid"/> row item used for preparing the ClipboardRowContent.
|
||||
/// </summary>
|
||||
public object Item
|
||||
{
|
||||
get
|
||||
{
|
||||
return _item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data for the <see cref="E:Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.LoadingRowDetails"/>, <see cref="E:Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.UnloadingRowDetails"/>,
|
||||
/// and <see cref="E:Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.RowDetailsVisibilityChanged"/> events.
|
||||
/// </summary>
|
||||
public class DataGridRowDetailsEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowDetailsEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="row">The row that the event occurs for.</param>
|
||||
/// <param name="detailsElement">The row details section as a framework element.</param>
|
||||
public DataGridRowDetailsEventArgs(DataGridRow row, FrameworkElement detailsElement)
|
||||
{
|
||||
this.Row = row;
|
||||
this.DetailsElement = detailsElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row details section as a framework element.
|
||||
/// </summary>
|
||||
public FrameworkElement DetailsElement
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row that the event occurs for.
|
||||
/// </summary>
|
||||
public DataGridRow Row
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// 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>
|
||||
/// Provides information just after a row has exited edit mode.
|
||||
/// </summary>
|
||||
public class DataGridRowEditEndedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowEditEndedEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="row">The row container of the cell container that has just exited edit mode.</param>
|
||||
/// <param name="editAction">The editing action that has been taken.</param>
|
||||
public DataGridRowEditEndedEventArgs(DataGridRow row, DataGridEditAction editAction)
|
||||
{
|
||||
this.Row = row;
|
||||
this.EditAction = editAction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the editing action that has been taken.
|
||||
/// </summary>
|
||||
public DataGridEditAction EditAction
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row container of the cell container that has just exited edit mode.
|
||||
/// </summary>
|
||||
public DataGridRow Row
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// 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.ComponentModel;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides information just before a row exits editing mode.
|
||||
/// </summary>
|
||||
public class DataGridRowEditEndingEventArgs : CancelEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowEditEndingEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="row">The row container of the cell container that is about to exit edit mode.</param>
|
||||
/// <param name="editAction">The editing action that will be taken.</param>
|
||||
public DataGridRowEditEndingEventArgs(DataGridRow row, DataGridEditAction editAction)
|
||||
{
|
||||
this.Row = row;
|
||||
this.EditAction = editAction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the editing action that will be taken.
|
||||
/// </summary>
|
||||
public DataGridEditAction EditAction
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row container of the cell container that is about to exit edit mode.
|
||||
/// </summary>
|
||||
public DataGridRow Row
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// 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>
|
||||
/// Provides data for <see cref="DataGrid"/> row-related events.
|
||||
/// </summary>
|
||||
public class DataGridRowEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dataGridRow">The row that the event occurs for.</param>
|
||||
public DataGridRowEventArgs(DataGridRow dataGridRow)
|
||||
{
|
||||
this.Row = dataGridRow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row that the event occurs for.
|
||||
/// </summary>
|
||||
public DataGridRow Row
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,749 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.Primitives;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.Utilities;
|
||||
using Microsoft.Toolkit.Uwp.UI.Utilities;
|
||||
using Microsoft.Toolkit.Uwp.Utilities;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the header of a <see cref="DataGrid"/> row group.
|
||||
/// </summary>
|
||||
[TemplatePart(Name = DataGridRow.DATAGRIDROW_elementRoot, Type = typeof(Panel))]
|
||||
[TemplatePart(Name = DataGridRow.DATAGRIDROW_elementRowHeader, Type = typeof(DataGridRowHeader))]
|
||||
[TemplatePart(Name = DATAGRIDROWGROUPHEADER_bottomGridLine, Type = typeof(Rectangle))]
|
||||
[TemplatePart(Name = DATAGRIDROWGROUPHEADER_expanderButton, Type = typeof(ToggleButton))]
|
||||
[TemplatePart(Name = DATAGRIDROWGROUPHEADER_indentSpacer, Type = typeof(FrameworkElement))]
|
||||
[TemplatePart(Name = DATAGRIDROWGROUPHEADER_itemCountElement, Type = typeof(TextBlock))]
|
||||
[TemplatePart(Name = DATAGRIDROWGROUPHEADER_propertyNameElement, Type = typeof(TextBlock))]
|
||||
[TemplatePart(Name = DATAGRIDROWGROUPHEADER_propertyValueElement, Type = typeof(TextBlock))]
|
||||
[StyleTypedProperty(Property = "HeaderStyle", StyleTargetType = typeof(DataGridRowHeader))]
|
||||
public class DataGridRowGroupHeader : Control
|
||||
{
|
||||
private const string DATAGRIDROWGROUPHEADER_bottomGridLine = "BottomGridLine";
|
||||
private const string DATAGRIDROWGROUPHEADER_expanderButton = "ExpanderButton";
|
||||
private const string DATAGRIDROWGROUPHEADER_indentSpacer = "IndentSpacer";
|
||||
private const string DATAGRIDROWGROUPHEADER_itemCountElement = "ItemCountElement";
|
||||
private const string DATAGRIDROWGROUPHEADER_propertyNameElement = "PropertyNameElement";
|
||||
private const string DATAGRIDROWGROUPHEADER_propertyValueElement = "PropertyValueElement";
|
||||
|
||||
private bool _areIsCheckedHandlersSuspended;
|
||||
private Rectangle _bottomGridLine;
|
||||
private ToggleButton _expanderButton;
|
||||
private FrameworkElement _indentSpacer;
|
||||
private TextBlock _itemCountElement;
|
||||
private TextBlock _propertyNameElement;
|
||||
private TextBlock _propertyValueElement;
|
||||
private Panel _rootElement;
|
||||
private double _totalIndent;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowGroupHeader"/> class.
|
||||
/// </summary>
|
||||
public DataGridRowGroupHeader()
|
||||
{
|
||||
DefaultStyleKey = typeof(DataGridRowGroupHeader);
|
||||
|
||||
this.AddHandler(UIElement.TappedEvent, new TappedEventHandler(DataGridRowGroupHeader_Tapped), true /*handledEventsToo*/);
|
||||
this.AddHandler(UIElement.DoubleTappedEvent, new DoubleTappedEventHandler(DataGridRowGroupHeader_DoubleTapped), true /*handledEventsToo*/);
|
||||
|
||||
this.PointerCanceled += new PointerEventHandler(DataGridRowGroupHeader_PointerCanceled);
|
||||
this.PointerEntered += new PointerEventHandler(DataGridRowGroupHeader_PointerEntered);
|
||||
this.PointerExited += new PointerEventHandler(DataGridRowGroupHeader_PointerExited);
|
||||
this.PointerMoved += new PointerEventHandler(DataGridRowGroupHeader_PointerMoved);
|
||||
this.PointerPressed += new PointerEventHandler(DataGridRowGroupHeader_PointerPressed);
|
||||
this.PointerReleased += new PointerEventHandler(DataGridRowGroupHeader_PointerReleased);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the style applied to the header cell of a <see cref="DataGridRowGroupHeader"/>.
|
||||
/// </summary>
|
||||
public Style HeaderStyle
|
||||
{
|
||||
get { return GetValue(HeaderStyleProperty) as Style; }
|
||||
set { SetValue(HeaderStyleProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dependency Property for HeaderStyle
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty HeaderStyleProperty =
|
||||
DependencyProperty.Register(
|
||||
"HeaderStyle",
|
||||
typeof(Style),
|
||||
typeof(DataGridRowGroupHeader),
|
||||
new PropertyMetadata(null, OnHeaderStylePropertyChanged));
|
||||
|
||||
private static void OnHeaderStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = d as DataGridRowGroupHeader;
|
||||
if (groupHeader.HeaderElement != null)
|
||||
{
|
||||
groupHeader.HeaderElement.EnsureStyle(e.OldValue as Style);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether the item count is visible.
|
||||
/// </summary>
|
||||
public Visibility ItemCountVisibility
|
||||
{
|
||||
get { return (Visibility)GetValue(ItemCountVisibilityProperty); }
|
||||
set { SetValue(ItemCountVisibilityProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty for ItemCountVisibility
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ItemCountVisibilityProperty =
|
||||
DependencyProperty.Register(
|
||||
"ItemCountVisibility",
|
||||
typeof(Visibility),
|
||||
typeof(DataGridRowGroupHeader),
|
||||
new PropertyMetadata(Visibility.Visible));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nesting level of the associated group.
|
||||
/// </summary>
|
||||
public int Level
|
||||
{
|
||||
get { return (int)GetValue(LevelProperty); }
|
||||
internal set { SetValue(LevelProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the Level dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty LevelProperty =
|
||||
DependencyProperty.Register(
|
||||
"Level",
|
||||
typeof(int),
|
||||
typeof(DataGridRowGroupHeader),
|
||||
new PropertyMetadata(0));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property that this <see cref="DataGrid"/> row is bound to.
|
||||
/// </summary>
|
||||
public string PropertyName
|
||||
{
|
||||
get { return GetValue(PropertyNameProperty) as string; }
|
||||
set { SetValue(PropertyNameProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty for PropertyName
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PropertyNameProperty =
|
||||
DependencyProperty.Register(
|
||||
"PropertyName",
|
||||
typeof(string),
|
||||
typeof(DataGridRowGroupHeader),
|
||||
new PropertyMetadata(null, OnPropertyNameChanged));
|
||||
|
||||
private static void OnPropertyNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = d as DataGridRowGroupHeader;
|
||||
groupHeader.UpdateTitleElements();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether the property name is visible.
|
||||
/// </summary>
|
||||
public Visibility PropertyNameVisibility
|
||||
{
|
||||
get { return (Visibility)GetValue(PropertyNameVisibilityProperty); }
|
||||
set { SetValue(PropertyNameVisibilityProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty for PropertyNameVisibility
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PropertyNameVisibilityProperty =
|
||||
DependencyProperty.Register(
|
||||
"PropertyNameVisibility",
|
||||
typeof(Visibility),
|
||||
typeof(DataGridRowGroupHeader),
|
||||
new PropertyMetadata(Visibility.Visible));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the property that this <see cref="DataGrid"/> row is bound to.
|
||||
/// </summary>
|
||||
public string PropertyValue
|
||||
{
|
||||
get { return GetValue(PropertyValueProperty) as string; }
|
||||
set { SetValue(PropertyValueProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty for PropertyName
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PropertyValueProperty =
|
||||
DependencyProperty.Register(
|
||||
"PropertyValue",
|
||||
typeof(string),
|
||||
typeof(DataGridRowGroupHeader),
|
||||
new PropertyMetadata(null, OnPropertyValueChanged));
|
||||
|
||||
private static void OnPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = d as DataGridRowGroupHeader;
|
||||
groupHeader.UpdateTitleElements();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates the amount that the
|
||||
/// children of the <see cref="DataGridRowGroupHeader"/> are indented.
|
||||
/// </summary>
|
||||
public double SublevelIndent
|
||||
{
|
||||
get { return (double)GetValue(SublevelIndentProperty); }
|
||||
set { SetValue(SublevelIndentProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SublevelIndent Dependency property
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty SublevelIndentProperty =
|
||||
DependencyProperty.Register(
|
||||
"SublevelIndent",
|
||||
typeof(double),
|
||||
typeof(DataGridRowGroupHeader),
|
||||
new PropertyMetadata(DataGrid.DATAGRID_defaultRowGroupSublevelIndent, OnSublevelIndentPropertyChanged));
|
||||
|
||||
private static void OnSublevelIndentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = d as DataGridRowGroupHeader;
|
||||
double newValue = (double)e.NewValue;
|
||||
|
||||
// We don't need to revert to the old value if our input is bad because we never read this property value
|
||||
if (double.IsNaN(newValue))
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueCannotBeSetToNAN("SublevelIndent");
|
||||
}
|
||||
else if (double.IsInfinity(newValue))
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("SublevelIndent");
|
||||
}
|
||||
else if (newValue < 0)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "SublevelIndent", 0);
|
||||
}
|
||||
|
||||
if (groupHeader.OwningGrid != null)
|
||||
{
|
||||
groupHeader.OwningGrid.OnSublevelIndentUpdated(groupHeader, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ICollectionViewGroup implementation associated with this <see cref="DataGridRowGroupHeader"/>.
|
||||
/// </summary>
|
||||
public ICollectionViewGroup CollectionViewGroup
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.RowGroupInfo == null ? null : this.RowGroupInfo.CollectionViewGroup;
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGridRowHeader HeaderCell
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.HeaderElement;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridRowHeader HeaderElement
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private bool IsCurrent
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(this.OwningGrid != null, "Expected non-null OwningGrid.");
|
||||
return this.RowGroupInfo.Slot == this.OwningGrid.CurrentSlot;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPointerOver
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private bool IsPressed
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal bool IsRecycled
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal DataGrid OwningGrid
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal DataGridRowGroupInfo RowGroupInfo
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
internal double TotalIndent
|
||||
{
|
||||
set
|
||||
{
|
||||
_totalIndent = value;
|
||||
if (_indentSpacer != null)
|
||||
{
|
||||
_indentSpacer.Width = _totalIndent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void ApplyHeaderState(bool animate)
|
||||
{
|
||||
if (this.HeaderElement != null && this.OwningGrid.AreRowHeadersVisible)
|
||||
{
|
||||
this.HeaderElement.ApplyOwnerState(animate);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ApplyState(bool useTransitions)
|
||||
{
|
||||
// Common States
|
||||
if (this.IsPressed)
|
||||
{
|
||||
VisualStates.GoToState(this, useTransitions, VisualStates.StatePressed, VisualStates.StatePointerOver, VisualStates.StateNormal);
|
||||
}
|
||||
else if (this.IsPointerOver)
|
||||
{
|
||||
VisualStates.GoToState(this, useTransitions, VisualStates.StatePointerOver, VisualStates.StateNormal);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, useTransitions, VisualStates.StateNormal);
|
||||
}
|
||||
|
||||
// Current States
|
||||
if (this.IsCurrent && !this.OwningGrid.ColumnHeaderHasFocus)
|
||||
{
|
||||
if (this.OwningGrid.ContainsFocus)
|
||||
{
|
||||
VisualStates.GoToState(this, useTransitions, VisualStates.StateCurrentWithFocus, VisualStates.StateCurrent, VisualStates.StateRegular);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, useTransitions, VisualStates.StateCurrent, VisualStates.StateRegular);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, useTransitions, VisualStates.StateRegular);
|
||||
}
|
||||
|
||||
// Expanded States
|
||||
if (this.RowGroupInfo.CollectionViewGroup.GroupItems.Count == 0)
|
||||
{
|
||||
VisualStates.GoToState(this, useTransitions, VisualStates.StateEmpty);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.RowGroupInfo.Visibility == Visibility.Visible)
|
||||
{
|
||||
VisualStates.GoToState(this, useTransitions, VisualStates.StateExpanded, VisualStates.StateEmpty);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, useTransitions, VisualStates.StateCollapsed, VisualStates.StateEmpty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ArrangeOverride
|
||||
/// </summary>
|
||||
/// <param name="finalSize">The final area within the parent that this object should use to arrange itself and its children.</param>
|
||||
/// <returns>The actual size that is used after the element is arranged in layout.</returns>
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (this.OwningGrid == null)
|
||||
{
|
||||
return base.ArrangeOverride(finalSize);
|
||||
}
|
||||
|
||||
Size size = base.ArrangeOverride(finalSize);
|
||||
if (_rootElement != null)
|
||||
{
|
||||
if (this.OwningGrid.AreRowGroupHeadersFrozen)
|
||||
{
|
||||
foreach (UIElement child in _rootElement.Children)
|
||||
{
|
||||
child.Clip = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double frozenLeftEdge = 0;
|
||||
foreach (UIElement child in _rootElement.Children)
|
||||
{
|
||||
if (DataGridFrozenGrid.GetIsFrozen(child) && child.Visibility == Visibility.Visible)
|
||||
{
|
||||
TranslateTransform transform = new TranslateTransform();
|
||||
|
||||
// Automatic layout rounding doesn't apply to transforms so we need to Round this
|
||||
transform.X = Math.Round(this.OwningGrid.HorizontalOffset);
|
||||
child.RenderTransform = transform;
|
||||
|
||||
double childLeftEdge = child.Translate(this, new Point(child.RenderSize.Width, 0)).X - transform.X;
|
||||
frozenLeftEdge = Math.Max(frozenLeftEdge, childLeftEdge + this.OwningGrid.HorizontalOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// Clip the non-frozen elements so they don't overlap the frozen ones
|
||||
foreach (UIElement child in _rootElement.Children)
|
||||
{
|
||||
if (!DataGridFrozenGrid.GetIsFrozen(child))
|
||||
{
|
||||
EnsureChildClip(child, frozenLeftEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
internal void ClearFrozenStates()
|
||||
{
|
||||
if (_rootElement != null)
|
||||
{
|
||||
foreach (UIElement child in _rootElement.Children)
|
||||
{
|
||||
child.RenderTransform = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DataGridRowGroupHeader_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
if (this.OwningGrid != null && !this.OwningGrid.HasColumnUserInteraction)
|
||||
{
|
||||
if (!e.Handled && this.OwningGrid.IsTabStop)
|
||||
{
|
||||
bool success = this.OwningGrid.Focus(FocusState.Programmatic);
|
||||
Debug.Assert(success, "Expected successful focus change.");
|
||||
}
|
||||
|
||||
e.Handled = this.OwningGrid.UpdateStateOnTapped(e, this.OwningGrid.CurrentColumnIndex, this.RowGroupInfo.Slot, false /*allowEdit*/);
|
||||
}
|
||||
}
|
||||
|
||||
private void DataGridRowGroupHeader_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
if (this.OwningGrid != null && !this.OwningGrid.HasColumnUserInteraction && !e.Handled)
|
||||
{
|
||||
ToggleExpandCollapse(this.RowGroupInfo.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible, true);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureChildClip(UIElement child, double frozenLeftEdge)
|
||||
{
|
||||
double childLeftEdge = child.Translate(this, new Point(0, 0)).X;
|
||||
if (frozenLeftEdge > childLeftEdge)
|
||||
{
|
||||
double xClip = Math.Round(frozenLeftEdge - childLeftEdge);
|
||||
RectangleGeometry rg = new RectangleGeometry();
|
||||
rg.Rect = new Rect(xClip, 0, Math.Max(0, child.RenderSize.Width - xClip), child.RenderSize.Height);
|
||||
child.Clip = rg;
|
||||
}
|
||||
else
|
||||
{
|
||||
child.Clip = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal void EnsureExpanderButtonIsChecked()
|
||||
{
|
||||
if (_expanderButton != null &&
|
||||
this.RowGroupInfo != null &&
|
||||
this.RowGroupInfo.CollectionViewGroup != null &&
|
||||
this.RowGroupInfo.CollectionViewGroup.GroupItems != null &&
|
||||
this.RowGroupInfo.CollectionViewGroup.GroupItems.Count != 0)
|
||||
{
|
||||
SetIsCheckedNoCallBack(this.RowGroupInfo.Visibility == Visibility.Visible);
|
||||
}
|
||||
}
|
||||
|
||||
internal void EnsureHeaderStyleAndVisibility(Style previousStyle)
|
||||
{
|
||||
if (this.HeaderElement != null && this.OwningGrid != null)
|
||||
{
|
||||
if (this.OwningGrid.AreRowHeadersVisible)
|
||||
{
|
||||
this.HeaderElement.EnsureStyle(previousStyle);
|
||||
this.HeaderElement.Visibility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.HeaderElement.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpanderButton_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!_areIsCheckedHandlersSuspended)
|
||||
{
|
||||
ToggleExpandCollapse(Visibility.Visible, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpanderButton_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!_areIsCheckedHandlersSuspended)
|
||||
{
|
||||
ToggleExpandCollapse(Visibility.Collapsed, true);
|
||||
}
|
||||
}
|
||||
|
||||
internal void LoadVisualsForDisplay()
|
||||
{
|
||||
EnsureExpanderButtonIsChecked();
|
||||
|
||||
EnsureHeaderStyleAndVisibility(null);
|
||||
ApplyState(false /*useTransitions*/);
|
||||
ApplyHeaderState(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the visual tree for the row group header when a new template is applied.
|
||||
/// </summary>
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
_rootElement = GetTemplateChild(DataGridRow.DATAGRIDROW_elementRoot) as Panel;
|
||||
|
||||
if (_expanderButton != null)
|
||||
{
|
||||
_expanderButton.Checked -= ExpanderButton_Checked;
|
||||
_expanderButton.Unchecked -= ExpanderButton_Unchecked;
|
||||
}
|
||||
|
||||
_bottomGridLine = GetTemplateChild(DATAGRIDROWGROUPHEADER_bottomGridLine) as Rectangle;
|
||||
|
||||
_expanderButton = GetTemplateChild(DATAGRIDROWGROUPHEADER_expanderButton) as ToggleButton;
|
||||
if (_expanderButton != null)
|
||||
{
|
||||
EnsureExpanderButtonIsChecked();
|
||||
_expanderButton.Checked += new RoutedEventHandler(ExpanderButton_Checked);
|
||||
_expanderButton.Unchecked += new RoutedEventHandler(ExpanderButton_Unchecked);
|
||||
}
|
||||
|
||||
this.HeaderElement = GetTemplateChild(DataGridRow.DATAGRIDROW_elementRowHeader) as DataGridRowHeader;
|
||||
if (this.HeaderElement != null)
|
||||
{
|
||||
this.HeaderElement.Owner = this;
|
||||
EnsureHeaderStyleAndVisibility(null);
|
||||
}
|
||||
|
||||
_indentSpacer = GetTemplateChild(DATAGRIDROWGROUPHEADER_indentSpacer) as FrameworkElement;
|
||||
if (_indentSpacer != null)
|
||||
{
|
||||
_indentSpacer.Width = _totalIndent;
|
||||
}
|
||||
|
||||
_itemCountElement = GetTemplateChild(DATAGRIDROWGROUPHEADER_itemCountElement) as TextBlock;
|
||||
_propertyNameElement = GetTemplateChild(DATAGRIDROWGROUPHEADER_propertyNameElement) as TextBlock;
|
||||
_propertyValueElement = GetTemplateChild(DATAGRIDROWGROUPHEADER_propertyValueElement) as TextBlock;
|
||||
UpdateTitleElements();
|
||||
EnsureGridLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
|
||||
/// </summary>
|
||||
/// <returns>An automation peer for this <see cref="DataGridRowGroupHeader"/>.</returns>
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new DataGridRowGroupHeaderAutomationPeer(this);
|
||||
}
|
||||
|
||||
private void SetIsCheckedNoCallBack(bool value)
|
||||
{
|
||||
if (_expanderButton != null && _expanderButton.IsChecked != value)
|
||||
{
|
||||
_areIsCheckedHandlersSuspended = true;
|
||||
try
|
||||
{
|
||||
_expanderButton.IsChecked = value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_areIsCheckedHandlersSuspended = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void ToggleExpandCollapse(Visibility newVisibility, bool setCurrent)
|
||||
{
|
||||
if (this.RowGroupInfo.CollectionViewGroup.GroupItems.Count != 0)
|
||||
{
|
||||
if (this.OwningGrid == null)
|
||||
{
|
||||
// Do these even if the OwningGrid is null in case it could improve the Designer experience for a standalone DataGridRowGroupHeader
|
||||
this.RowGroupInfo.Visibility = newVisibility;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.OwningGrid.OnRowGroupHeaderToggled(this, newVisibility, setCurrent);
|
||||
}
|
||||
|
||||
EnsureExpanderButtonIsChecked();
|
||||
ApplyState(true /*useTransitions*/);
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateTitleElements()
|
||||
{
|
||||
string propertyName = this.PropertyName;
|
||||
bool hasPropertyValue = _propertyValueElement != null && !string.IsNullOrEmpty(this.PropertyValue);
|
||||
|
||||
if (_propertyNameElement != null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(propertyName) && this.OwningGrid.DataConnection.DataType != null)
|
||||
{
|
||||
string displayName = this.OwningGrid.DataConnection.DataType.GetDisplayName(propertyName);
|
||||
if (!string.IsNullOrWhiteSpace(displayName))
|
||||
{
|
||||
propertyName = displayName;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
propertyName = this.OwningGrid.RowGroupHeaderPropertyNameAlternative;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(propertyName) && hasPropertyValue)
|
||||
{
|
||||
propertyName = string.Format(CultureInfo.CurrentCulture, Properties.Resources.DataGridRowGroupHeader_PropertyName, propertyName);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
_propertyNameElement.Text = propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPropertyValue)
|
||||
{
|
||||
_propertyValueElement.Text = this.PropertyValue;
|
||||
}
|
||||
|
||||
if (_itemCountElement != null && this.RowGroupInfo != null && this.RowGroupInfo.CollectionViewGroup != null)
|
||||
{
|
||||
_itemCountElement.Text = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
this.RowGroupInfo.CollectionViewGroup.GroupItems.Count == 1 ? Properties.Resources.DataGridRowGroupHeader_ItemCountSingular : Properties.Resources.DataGridRowGroupHeader_ItemCountPlural,
|
||||
this.RowGroupInfo.CollectionViewGroup.GroupItems.Count);
|
||||
}
|
||||
}
|
||||
|
||||
private void DataGridRowGroupHeader_PointerCanceled(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateIsPointerOver(false);
|
||||
UpdateIsPressed(false);
|
||||
}
|
||||
|
||||
private void DataGridRowGroupHeader_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateIsPointerOver(true);
|
||||
}
|
||||
|
||||
private void DataGridRowGroupHeader_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateIsPointerOver(false);
|
||||
}
|
||||
|
||||
private void DataGridRowGroupHeader_PointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateIsPointerOver(true);
|
||||
}
|
||||
|
||||
private void DataGridRowGroupHeader_PointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateIsPressed(true);
|
||||
}
|
||||
|
||||
private void DataGridRowGroupHeader_PointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateIsPressed(false);
|
||||
}
|
||||
|
||||
internal void EnsureGridLine()
|
||||
{
|
||||
if (this.OwningGrid != null && _bottomGridLine != null)
|
||||
{
|
||||
Visibility newVisibility = this.OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.Horizontal || this.OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.All
|
||||
? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
if (newVisibility != _bottomGridLine.Visibility)
|
||||
{
|
||||
_bottomGridLine.Visibility = newVisibility;
|
||||
}
|
||||
|
||||
_bottomGridLine.Fill = this.OwningGrid.HorizontalGridLinesBrush;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateIsPointerOver(bool isPointerOver)
|
||||
{
|
||||
if (!this.IsEnabled || isPointerOver == this.IsPointerOver)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.IsPointerOver = isPointerOver;
|
||||
ApplyState(true /*useTransitions*/);
|
||||
}
|
||||
|
||||
private void UpdateIsPressed(bool isPressed)
|
||||
{
|
||||
if (!this.IsEnabled || isPressed == this.IsPressed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.IsPressed = isPressed;
|
||||
ApplyState(true /*useTransitions*/);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// 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>
|
||||
/// EventArgs used for the DataGrid's LoadingRowGroup and UnloadingRowGroup events
|
||||
/// </summary>
|
||||
public class DataGridRowGroupHeaderEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowGroupHeaderEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="rowGroupHeader">The row group header that the event occurs for.</param>
|
||||
public DataGridRowGroupHeaderEventArgs(DataGridRowGroupHeader rowGroupHeader)
|
||||
{
|
||||
this.RowGroupHeader = rowGroupHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="DataGridRowGroupHeader"/> associated with this instance.
|
||||
/// </summary>
|
||||
public DataGridRowGroupHeader RowGroupHeader
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// 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.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals
|
||||
{
|
||||
internal class DataGridRowGroupInfo
|
||||
{
|
||||
public DataGridRowGroupInfo(
|
||||
ICollectionViewGroup collectionViewGroup,
|
||||
Visibility visibility,
|
||||
int level,
|
||||
int slot,
|
||||
int lastSubItemSlot)
|
||||
{
|
||||
this.CollectionViewGroup = collectionViewGroup;
|
||||
this.Visibility = visibility;
|
||||
this.Level = level;
|
||||
this.Slot = slot;
|
||||
this.LastSubItemSlot = lastSubItemSlot;
|
||||
}
|
||||
|
||||
public ICollectionViewGroup CollectionViewGroup
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public int LastSubItemSlot
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int Level
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public int Slot
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Visibility Visibility
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,443 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.Utilities;
|
||||
using Microsoft.Toolkit.Uwp.UI.Utilities;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an individual <see cref="DataGrid"/> row header.
|
||||
/// </summary>
|
||||
[TemplatePart(Name = DATAGRIDROWHEADER_elementRootName, Type = typeof(FrameworkElement))]
|
||||
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateNormal, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateNormalCurrentRow, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateNormalEditingRow, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateNormalEditingRowFocused, GroupName = VisualStates.GroupCommon)]
|
||||
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_statePointerOver, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_statePointerOverCurrentRow, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_statePointerOverEditingRow, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_statePointerOverEditingRowFocused, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_statePointerOverSelected, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_statePointerOverSelectedFocused, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_statePointerOverSelectedCurrentRow, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_statePointerOverSelectedCurrentRowFocused, GroupName = VisualStates.GroupCommon)]
|
||||
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateSelected, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateSelectedCurrentRow, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateSelectedCurrentRowFocused, GroupName = VisualStates.GroupCommon)]
|
||||
[TemplateVisualState(Name = DATAGRIDROWHEADER_stateSelectedFocused, GroupName = VisualStates.GroupCommon)]
|
||||
|
||||
[TemplateVisualState(Name = VisualStates.StateRowInvalid, GroupName = VisualStates.GroupValidation)]
|
||||
[TemplateVisualState(Name = VisualStates.StateRowValid, GroupName = VisualStates.GroupValidation)]
|
||||
public partial class DataGridRowHeader : ContentControl
|
||||
{
|
||||
private const string DATAGRIDROWHEADER_elementRootName = "RowHeaderRoot";
|
||||
private const double DATAGRIDROWHEADER_separatorThickness = 1;
|
||||
|
||||
private const string DATAGRIDROWHEADER_statePointerOver = "PointerOver";
|
||||
private const string DATAGRIDROWHEADER_statePointerOverCurrentRow = "PointerOverCurrentRow";
|
||||
private const string DATAGRIDROWHEADER_statePointerOverEditingRow = "PointerOverUnfocusedEditingRow";
|
||||
private const string DATAGRIDROWHEADER_statePointerOverEditingRowFocused = "PointerOverEditingRow";
|
||||
private const string DATAGRIDROWHEADER_statePointerOverSelected = "PointerOverUnfocusedSelected";
|
||||
private const string DATAGRIDROWHEADER_statePointerOverSelectedCurrentRow = "PointerOverUnfocusedCurrentRowSelected";
|
||||
private const string DATAGRIDROWHEADER_statePointerOverSelectedCurrentRowFocused = "PointerOverCurrentRowSelected";
|
||||
private const string DATAGRIDROWHEADER_statePointerOverSelectedFocused = "PointerOverSelected";
|
||||
private const string DATAGRIDROWHEADER_stateNormal = "Normal";
|
||||
private const string DATAGRIDROWHEADER_stateNormalCurrentRow = "NormalCurrentRow";
|
||||
private const string DATAGRIDROWHEADER_stateNormalEditingRow = "UnfocusedEditingRow";
|
||||
private const string DATAGRIDROWHEADER_stateNormalEditingRowFocused = "NormalEditingRow";
|
||||
private const string DATAGRIDROWHEADER_stateSelected = "UnfocusedSelected";
|
||||
private const string DATAGRIDROWHEADER_stateSelectedCurrentRow = "UnfocusedCurrentRowSelected";
|
||||
private const string DATAGRIDROWHEADER_stateSelectedCurrentRowFocused = "NormalCurrentRowSelected";
|
||||
private const string DATAGRIDROWHEADER_stateSelectedFocused = "NormalSelected";
|
||||
|
||||
private const byte DATAGRIDROWHEADER_statePointerOverCode = 0;
|
||||
private const byte DATAGRIDROWHEADER_statePointerOverCurrentRowCode = 1;
|
||||
private const byte DATAGRIDROWHEADER_statePointerOverEditingRowCode = 2;
|
||||
private const byte DATAGRIDROWHEADER_statePointerOverEditingRowFocusedCode = 3;
|
||||
private const byte DATAGRIDROWHEADER_statePointerOverSelectedCode = 4;
|
||||
private const byte DATAGRIDROWHEADER_statePointerOverSelectedCurrentRowCode = 5;
|
||||
private const byte DATAGRIDROWHEADER_statePointerOverSelectedCurrentRowFocusedCode = 6;
|
||||
private const byte DATAGRIDROWHEADER_statePointerOverSelectedFocusedCode = 7;
|
||||
private const byte DATAGRIDROWHEADER_stateNormalCode = 8;
|
||||
private const byte DATAGRIDROWHEADER_stateNormalCurrentRowCode = 9;
|
||||
private const byte DATAGRIDROWHEADER_stateNormalEditingRowCode = 10;
|
||||
private const byte DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode = 11;
|
||||
private const byte DATAGRIDROWHEADER_stateSelectedCode = 12;
|
||||
private const byte DATAGRIDROWHEADER_stateSelectedCurrentRowCode = 13;
|
||||
private const byte DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedCode = 14;
|
||||
private const byte DATAGRIDROWHEADER_stateSelectedFocusedCode = 15;
|
||||
private const byte DATAGRIDROWHEADER_stateNullCode = 255;
|
||||
|
||||
private static byte[] _fallbackStateMapping = new byte[]
|
||||
{
|
||||
DATAGRIDROWHEADER_stateNormalCode,
|
||||
DATAGRIDROWHEADER_stateNormalCurrentRowCode,
|
||||
DATAGRIDROWHEADER_statePointerOverEditingRowFocusedCode,
|
||||
DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode,
|
||||
DATAGRIDROWHEADER_statePointerOverSelectedFocusedCode,
|
||||
DATAGRIDROWHEADER_statePointerOverSelectedCurrentRowFocusedCode,
|
||||
DATAGRIDROWHEADER_stateSelectedFocusedCode,
|
||||
DATAGRIDROWHEADER_stateSelectedFocusedCode,
|
||||
DATAGRIDROWHEADER_stateNullCode,
|
||||
DATAGRIDROWHEADER_stateNormalCode,
|
||||
DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode,
|
||||
DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedCode,
|
||||
DATAGRIDROWHEADER_stateSelectedFocusedCode,
|
||||
DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedCode,
|
||||
DATAGRIDROWHEADER_stateNormalCurrentRowCode,
|
||||
DATAGRIDROWHEADER_stateNormalCode,
|
||||
};
|
||||
|
||||
private static byte[] _idealStateMapping = new byte[]
|
||||
{
|
||||
DATAGRIDROWHEADER_stateNormalCode,
|
||||
DATAGRIDROWHEADER_stateNormalCode,
|
||||
DATAGRIDROWHEADER_statePointerOverCode,
|
||||
DATAGRIDROWHEADER_statePointerOverCode,
|
||||
DATAGRIDROWHEADER_stateNullCode,
|
||||
DATAGRIDROWHEADER_stateNullCode,
|
||||
DATAGRIDROWHEADER_stateNullCode,
|
||||
DATAGRIDROWHEADER_stateNullCode,
|
||||
DATAGRIDROWHEADER_stateSelectedCode,
|
||||
DATAGRIDROWHEADER_stateSelectedFocusedCode,
|
||||
DATAGRIDROWHEADER_statePointerOverSelectedCode,
|
||||
DATAGRIDROWHEADER_statePointerOverSelectedFocusedCode,
|
||||
DATAGRIDROWHEADER_stateNormalEditingRowCode,
|
||||
DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode,
|
||||
DATAGRIDROWHEADER_statePointerOverEditingRowCode,
|
||||
DATAGRIDROWHEADER_statePointerOverEditingRowFocusedCode,
|
||||
DATAGRIDROWHEADER_stateNormalCurrentRowCode,
|
||||
DATAGRIDROWHEADER_stateNormalCurrentRowCode,
|
||||
DATAGRIDROWHEADER_statePointerOverCurrentRowCode,
|
||||
DATAGRIDROWHEADER_statePointerOverCurrentRowCode,
|
||||
DATAGRIDROWHEADER_stateNullCode,
|
||||
DATAGRIDROWHEADER_stateNullCode,
|
||||
DATAGRIDROWHEADER_stateNullCode,
|
||||
DATAGRIDROWHEADER_stateNullCode,
|
||||
DATAGRIDROWHEADER_stateSelectedCurrentRowCode,
|
||||
DATAGRIDROWHEADER_stateSelectedCurrentRowFocusedCode,
|
||||
DATAGRIDROWHEADER_statePointerOverSelectedCurrentRowCode,
|
||||
DATAGRIDROWHEADER_statePointerOverSelectedCurrentRowFocusedCode,
|
||||
DATAGRIDROWHEADER_stateNormalEditingRowCode,
|
||||
DATAGRIDROWHEADER_stateNormalEditingRowFocusedCode,
|
||||
DATAGRIDROWHEADER_statePointerOverEditingRowCode,
|
||||
DATAGRIDROWHEADER_statePointerOverEditingRowFocusedCode
|
||||
};
|
||||
|
||||
private static string[] _stateNames = new string[]
|
||||
{
|
||||
DATAGRIDROWHEADER_statePointerOver,
|
||||
DATAGRIDROWHEADER_statePointerOverCurrentRow,
|
||||
DATAGRIDROWHEADER_statePointerOverEditingRow,
|
||||
DATAGRIDROWHEADER_statePointerOverEditingRowFocused,
|
||||
DATAGRIDROWHEADER_statePointerOverSelected,
|
||||
DATAGRIDROWHEADER_statePointerOverSelectedCurrentRow,
|
||||
DATAGRIDROWHEADER_statePointerOverSelectedCurrentRowFocused,
|
||||
DATAGRIDROWHEADER_statePointerOverSelectedFocused,
|
||||
DATAGRIDROWHEADER_stateNormal,
|
||||
DATAGRIDROWHEADER_stateNormalCurrentRow,
|
||||
DATAGRIDROWHEADER_stateNormalEditingRow,
|
||||
DATAGRIDROWHEADER_stateNormalEditingRowFocused,
|
||||
DATAGRIDROWHEADER_stateSelected,
|
||||
DATAGRIDROWHEADER_stateSelectedCurrentRow,
|
||||
DATAGRIDROWHEADER_stateSelectedCurrentRowFocused,
|
||||
DATAGRIDROWHEADER_stateSelectedFocused
|
||||
};
|
||||
|
||||
private FrameworkElement _rootElement;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowHeader"/> class.
|
||||
/// </summary>
|
||||
public DataGridRowHeader()
|
||||
{
|
||||
this.IsTapEnabled = true;
|
||||
|
||||
this.AddHandler(UIElement.TappedEvent, new TappedEventHandler(DataGridRowHeader_Tapped), true /*handledEventsToo*/);
|
||||
|
||||
DefaultStyleKey = typeof(DataGridRowHeader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="T:System.Windows.Media.Brush"/> used to paint the row header separator lines.
|
||||
/// </summary>
|
||||
public Brush SeparatorBrush
|
||||
{
|
||||
get { return GetValue(SeparatorBrushProperty) as Brush; }
|
||||
set { SetValue(SeparatorBrushProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DataGridRowHeader.SeparatorBrush"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty SeparatorBrushProperty =
|
||||
DependencyProperty.Register(
|
||||
"SeparatorBrush",
|
||||
typeof(Brush),
|
||||
typeof(DataGridRowHeader),
|
||||
null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the row header separator lines are visible.
|
||||
/// </summary>
|
||||
public Visibility SeparatorVisibility
|
||||
{
|
||||
get { return (Visibility)GetValue(SeparatorVisibilityProperty); }
|
||||
set { SetValue(SeparatorVisibilityProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Microsoft.Toolkit.Uwp.UI.Controls.Primitives.DataGridRowHeader.SeparatorVisibility"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty SeparatorVisibilityProperty =
|
||||
DependencyProperty.Register(
|
||||
"SeparatorVisibility",
|
||||
typeof(Visibility),
|
||||
typeof(DataGridRowHeader),
|
||||
new PropertyMetadata(Visibility.Visible));
|
||||
|
||||
private DataGrid OwningGrid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningRow != null)
|
||||
{
|
||||
return this.OwningRow.OwningGrid;
|
||||
}
|
||||
else if (this.OwningRowGroupHeader != null)
|
||||
{
|
||||
return this.OwningRowGroupHeader.OwningGrid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridRow OwningRow
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Owner as DataGridRow;
|
||||
}
|
||||
}
|
||||
|
||||
private DataGridRowGroupHeader OwningRowGroupHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Owner as DataGridRowGroupHeader;
|
||||
}
|
||||
}
|
||||
|
||||
internal Control Owner
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private int Slot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.OwningRow != null)
|
||||
{
|
||||
return this.OwningRow.Slot;
|
||||
}
|
||||
else if (this.OwningRowGroupHeader != null)
|
||||
{
|
||||
return this.OwningRowGroupHeader.RowGroupInfo.Slot;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the visual tree for the row header when a new template is applied.
|
||||
/// </summary>
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
_rootElement = GetTemplateChild(DATAGRIDROWHEADER_elementRootName) as FrameworkElement;
|
||||
if (_rootElement != null)
|
||||
{
|
||||
ApplyOwnerState(false /*animate*/);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures the children of a <see cref="T:System.Windows.Controls.Primitives.DataGridRowHeader"/> to prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)"/> pass.
|
||||
/// </summary>
|
||||
/// <param name="availableSize">
|
||||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The size that the <see cref="T:System.Windows.Controls.Primitives.DataGridRowHeader"/> determines it needs during layout, based on its calculations of child object allocated sizes.
|
||||
/// </returns>
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (this.OwningRow == null || this.OwningGrid == null)
|
||||
{
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
double measureHeight = double.IsNaN(this.OwningGrid.RowHeight) ? availableSize.Height : this.OwningGrid.RowHeight;
|
||||
double measureWidth = double.IsNaN(this.OwningGrid.RowHeaderWidth) ? availableSize.Width : this.OwningGrid.RowHeaderWidth;
|
||||
Size measuredSize = base.MeasureOverride(new Size(measureWidth, measureHeight));
|
||||
|
||||
// Auto grow the row header or force it to a fixed width based on the DataGrid's setting
|
||||
if (!double.IsNaN(this.OwningGrid.RowHeaderWidth) || measuredSize.Width < this.OwningGrid.ActualRowHeaderWidth)
|
||||
{
|
||||
return new Size(this.OwningGrid.ActualRowHeaderWidth, measuredSize.Height);
|
||||
}
|
||||
|
||||
return measuredSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
|
||||
/// </summary>
|
||||
/// <returns>An automation peer for this <see cref="T:System.Windows.Controls.Primitives.DataGridRowHeader"/>.</returns>
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new DataGridRowHeaderAutomationPeer(this);
|
||||
}
|
||||
|
||||
internal void ApplyOwnerState(bool animate)
|
||||
{
|
||||
if (_rootElement != null && this.Owner != null && this.Owner.Visibility == Visibility.Visible)
|
||||
{
|
||||
byte idealStateMappingIndex = 0;
|
||||
|
||||
if (this.OwningRow != null)
|
||||
{
|
||||
if (this.OwningRow.IsValid)
|
||||
{
|
||||
VisualStates.GoToState(this, true, VisualStates.StateRowValid);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStates.GoToState(this, true, VisualStates.StateRowInvalid, VisualStates.StateRowValid);
|
||||
}
|
||||
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
if (this.OwningGrid.CurrentSlot == this.OwningRow.Slot)
|
||||
{
|
||||
idealStateMappingIndex += 16;
|
||||
}
|
||||
|
||||
if (this.OwningGrid.ContainsFocus)
|
||||
{
|
||||
idealStateMappingIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.OwningRow.IsSelected || this.OwningRow.IsEditing)
|
||||
{
|
||||
idealStateMappingIndex += 8;
|
||||
}
|
||||
|
||||
if (this.OwningRow.IsEditing)
|
||||
{
|
||||
idealStateMappingIndex += 4;
|
||||
}
|
||||
|
||||
if (this.OwningRow.IsPointerOver)
|
||||
{
|
||||
idealStateMappingIndex += 2;
|
||||
}
|
||||
}
|
||||
else if (this.OwningRowGroupHeader != null && this.OwningGrid != null && this.OwningGrid.CurrentSlot == this.OwningRowGroupHeader.RowGroupInfo.Slot)
|
||||
{
|
||||
idealStateMappingIndex += 16;
|
||||
}
|
||||
|
||||
byte stateCode = _idealStateMapping[idealStateMappingIndex];
|
||||
Debug.Assert(stateCode != DATAGRIDROWHEADER_stateNullCode, "Expected stateCode other than DATAGRIDROWHEADER_stateNullCode.");
|
||||
|
||||
string storyboardName;
|
||||
while (stateCode != DATAGRIDROWHEADER_stateNullCode)
|
||||
{
|
||||
storyboardName = _stateNames[stateCode];
|
||||
if (VisualStateManager.GoToState(this, storyboardName, animate))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The state wasn't implemented so fall back to the next one
|
||||
stateCode = _fallbackStateMapping[stateCode];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the correct Style is applied to this object.
|
||||
/// </summary>
|
||||
/// <param name="previousStyle">Caller's previous associated Style</param>
|
||||
internal void EnsureStyle(Style previousStyle)
|
||||
{
|
||||
if (this.Style != null &&
|
||||
this.OwningRow != null &&
|
||||
this.Style != this.OwningRow.HeaderStyle &&
|
||||
this.OwningRowGroupHeader != null &&
|
||||
this.Style != this.OwningRowGroupHeader.HeaderStyle &&
|
||||
this.OwningGrid != null &&
|
||||
this.Style != this.OwningGrid.RowHeaderStyle &&
|
||||
this.Style != previousStyle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Style style = null;
|
||||
if (this.OwningRow != null)
|
||||
{
|
||||
style = this.OwningRow.HeaderStyle;
|
||||
}
|
||||
|
||||
if (style == null && this.OwningGrid != null)
|
||||
{
|
||||
style = this.OwningGrid.RowHeaderStyle;
|
||||
}
|
||||
|
||||
this.SetStyleWithType(style);
|
||||
}
|
||||
|
||||
private void DataGridRowHeader_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
if (this.OwningGrid != null && !this.OwningGrid.HasColumnUserInteraction)
|
||||
{
|
||||
if (!e.Handled && this.OwningGrid.IsTabStop)
|
||||
{
|
||||
bool success = this.OwningGrid.Focus(FocusState.Programmatic);
|
||||
Debug.Assert(success, "Expected successful focus change.");
|
||||
}
|
||||
|
||||
if (this.OwningRow != null)
|
||||
{
|
||||
Debug.Assert(sender is DataGridRowHeader, "Expected sender is DataGridRowHeader.");
|
||||
Debug.Assert(sender as ContentControl == this, "Expected sender is this.");
|
||||
|
||||
e.Handled = this.OwningGrid.UpdateStateOnTapped(e, -1, this.Slot, false /*allowEdit*/);
|
||||
this.OwningGrid.UpdatedStateOnTapped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,234 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Automation.Peers;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Used within the template of a <see cref="DataGrid"/> to specify the
|
||||
/// location in the control's visual tree where the rows are to be added.
|
||||
/// </summary>
|
||||
public sealed class DataGridRowsPresenter : Panel
|
||||
{
|
||||
private double _preManipulationHorizontalOffset;
|
||||
private double _preManipulationVerticalOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridRowsPresenter"/> class.
|
||||
/// </summary>
|
||||
public DataGridRowsPresenter()
|
||||
{
|
||||
this.ManipulationStarting += new ManipulationStartingEventHandler(DataGridRowsPresenter_ManipulationStarting);
|
||||
this.ManipulationStarted += new ManipulationStartedEventHandler(DataGridRowsPresenter_ManipulationStarted);
|
||||
this.ManipulationDelta += new ManipulationDeltaEventHandler(DataGridRowsPresenter_ManipulationDelta);
|
||||
}
|
||||
|
||||
internal DataGrid OwningGrid
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arranges the content of the <see cref="DataGridRowsPresenter"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The actual size used by the <see cref="DataGridRowsPresenter"/>.
|
||||
/// </returns>
|
||||
/// <param name="finalSize">
|
||||
/// The final area within the parent that this element should use to arrange itself and its children.
|
||||
/// </param>
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (finalSize.Height == 0 || this.OwningGrid == null)
|
||||
{
|
||||
return base.ArrangeOverride(finalSize);
|
||||
}
|
||||
|
||||
this.OwningGrid.OnFillerColumnWidthNeeded(finalSize.Width);
|
||||
|
||||
double rowDesiredWidth = this.OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth + this.OwningGrid.ColumnsInternal.FillerColumn.FillerWidth;
|
||||
double topEdge = -this.OwningGrid.NegVerticalOffset;
|
||||
foreach (UIElement element in this.OwningGrid.DisplayData.GetScrollingElements())
|
||||
{
|
||||
DataGridRow row = element as DataGridRow;
|
||||
if (row != null)
|
||||
{
|
||||
Debug.Assert(row.Index != -1, "Expected Index other than -1."); // A displayed row should always have its index
|
||||
|
||||
// Visibility for all filler cells needs to be set in one place. Setting it individually in
|
||||
// each CellsPresenter causes an NxN layout cycle (see DevDiv Bugs 211557)
|
||||
row.EnsureFillerVisibility();
|
||||
row.Arrange(new Rect(-this.OwningGrid.HorizontalOffset, topEdge, rowDesiredWidth, element.DesiredSize.Height));
|
||||
}
|
||||
else
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = element as DataGridRowGroupHeader;
|
||||
if (groupHeader != null)
|
||||
{
|
||||
double leftEdge = this.OwningGrid.AreRowGroupHeadersFrozen ? 0 : -this.OwningGrid.HorizontalOffset;
|
||||
groupHeader.Arrange(new Rect(leftEdge, topEdge, rowDesiredWidth - leftEdge, element.DesiredSize.Height));
|
||||
}
|
||||
}
|
||||
|
||||
topEdge += element.DesiredSize.Height;
|
||||
}
|
||||
|
||||
double finalHeight = Math.Max(topEdge + this.OwningGrid.NegVerticalOffset, finalSize.Height);
|
||||
|
||||
// Clip the RowsPresenter so rows cannot overlap other elements in certain styling scenarios
|
||||
RectangleGeometry rg = new RectangleGeometry();
|
||||
rg.Rect = new Rect(0, 0, finalSize.Width, finalHeight);
|
||||
this.Clip = rg;
|
||||
|
||||
return new Size(finalSize.Width, finalHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures the children of a <see cref="DataGridRowsPresenter"/> to
|
||||
/// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)"/> pass.
|
||||
/// </summary>
|
||||
/// <param name="availableSize">
|
||||
/// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The size that the <see cref="DataGridRowsPresenter"/> determines it needs during layout, based on its calculations of child object allocated sizes.
|
||||
/// </returns>
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (availableSize.Height == 0 || this.OwningGrid == null)
|
||||
{
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
// If the Width of our RowsPresenter changed then we need to invalidate our rows
|
||||
bool invalidateRows =
|
||||
(!this.OwningGrid.RowsPresenterAvailableSize.HasValue || availableSize.Width != this.OwningGrid.RowsPresenterAvailableSize.Value.Width) &&
|
||||
!double.IsInfinity(availableSize.Width);
|
||||
|
||||
// The DataGrid uses the RowsPresenter available size in order to autogrow
|
||||
// and calculate the scrollbars
|
||||
this.OwningGrid.RowsPresenterAvailableSize = availableSize;
|
||||
|
||||
this.OwningGrid.OnRowsMeasure();
|
||||
|
||||
double totalHeight = -this.OwningGrid.NegVerticalOffset;
|
||||
double totalCellsWidth = this.OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth;
|
||||
|
||||
double headerWidth = 0;
|
||||
foreach (UIElement element in this.OwningGrid.DisplayData.GetScrollingElements())
|
||||
{
|
||||
DataGridRow row = element as DataGridRow;
|
||||
if (row != null)
|
||||
{
|
||||
if (invalidateRows)
|
||||
{
|
||||
row.InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
|
||||
if (row != null && row.HeaderCell != null)
|
||||
{
|
||||
headerWidth = Math.Max(headerWidth, row.HeaderCell.DesiredSize.Width);
|
||||
}
|
||||
else
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = element as DataGridRowGroupHeader;
|
||||
if (groupHeader != null && groupHeader.HeaderCell != null)
|
||||
{
|
||||
headerWidth = Math.Max(headerWidth, groupHeader.HeaderCell.DesiredSize.Width);
|
||||
}
|
||||
}
|
||||
|
||||
totalHeight += element.DesiredSize.Height;
|
||||
}
|
||||
|
||||
this.OwningGrid.RowHeadersDesiredWidth = headerWidth;
|
||||
|
||||
// Could be positive infinity depending on the DataGrid's bounds
|
||||
this.OwningGrid.AvailableSlotElementRoom = availableSize.Height - totalHeight;
|
||||
|
||||
// TODO: totalHeight can be negative if we've just collapsed details. This is a workaround,
|
||||
// the real fix is to correct NegVerticalOffset.
|
||||
totalHeight = Math.Max(0, totalHeight);
|
||||
|
||||
return new Size(totalCellsWidth + headerWidth, totalHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
|
||||
/// </summary>
|
||||
/// <returns>An automation peer for this <see cref="DataGridRowsPresenter"/>.</returns>
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new DataGridRowsPresenterAutomationPeer(this);
|
||||
}
|
||||
|
||||
private void DataGridRowsPresenter_ManipulationStarting(object sender, ManipulationStartingRoutedEventArgs e)
|
||||
{
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
Debug.Assert(this.OwningGrid.IsEnabled, "Expected OwningGrid.IsEnabled is true.");
|
||||
|
||||
_preManipulationHorizontalOffset = this.OwningGrid.HorizontalOffset;
|
||||
_preManipulationVerticalOffset = this.OwningGrid.VerticalOffset;
|
||||
}
|
||||
}
|
||||
|
||||
private void DataGridRowsPresenter_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
|
||||
{
|
||||
if (e.PointerDeviceType != Windows.Devices.Input.PointerDeviceType.Touch)
|
||||
{
|
||||
e.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
private void DataGridRowsPresenter_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
|
||||
{
|
||||
if (this.OwningGrid != null)
|
||||
{
|
||||
e.Handled =
|
||||
this.OwningGrid.ProcessScrollOffsetDelta(_preManipulationHorizontalOffset - e.Cumulative.Translation.X - this.OwningGrid.HorizontalOffset, true /*isForHorizontalScroll*/) ||
|
||||
this.OwningGrid.ProcessScrollOffsetDelta(_preManipulationVerticalOffset - e.Cumulative.Translation.Y - this.OwningGrid.VerticalOffset, false /*isForHorizontalScroll*/);
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
internal void PrintChildren()
|
||||
{
|
||||
foreach (UIElement element in this.Children)
|
||||
{
|
||||
DataGridRow row = element as DataGridRow;
|
||||
if (row != null)
|
||||
{
|
||||
Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} Row: {1} Visibility: {2} ", row.Slot, row.Index, row.Visibility));
|
||||
}
|
||||
else
|
||||
{
|
||||
DataGridRowGroupHeader groupHeader = element as DataGridRowGroupHeader;
|
||||
if (groupHeader != null)
|
||||
{
|
||||
#if FEATURE_ICOLLECTIONVIEW_GROUP
|
||||
Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} GroupHeader: {1} Visibility: {2}", groupHeader.RowGroupInfo.Slot, groupHeader.RowGroupInfo.CollectionViewGroup.Name, groupHeader.Visibility));
|
||||
#else
|
||||
Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} Visibility: {1}", groupHeader.RowGroupInfo.Slot, groupHeader.Visibility));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,494 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Microsoft.Toolkit.Uwp.Utilities;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
internal class DataGridSelectedItemsCollection : IList
|
||||
{
|
||||
private List<object> _oldSelectedItemsCache;
|
||||
private IndexToValueTable<bool> _oldSelectedSlotsTable;
|
||||
private List<object> _selectedItemsCache;
|
||||
private IndexToValueTable<bool> _selectedSlotsTable;
|
||||
|
||||
public DataGridSelectedItemsCollection(DataGrid owningGrid)
|
||||
{
|
||||
this.OwningGrid = owningGrid;
|
||||
_oldSelectedItemsCache = new List<object>();
|
||||
_oldSelectedSlotsTable = new IndexToValueTable<bool>();
|
||||
_selectedItemsCache = new List<object>();
|
||||
_selectedSlotsTable = new IndexToValueTable<bool>();
|
||||
}
|
||||
|
||||
public object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= _selectedSlotsTable.IndexCount)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueMustBeBetween("index", "Index", 0, true, _selectedSlotsTable.IndexCount, false);
|
||||
}
|
||||
|
||||
int slot = _selectedSlotsTable.GetNthIndex(index);
|
||||
Debug.Assert(slot >= 0, "Expected positive slot.");
|
||||
return this.OwningGrid.DataConnection.GetDataItem(this.OwningGrid.RowIndexFromSlot(slot));
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFixedSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int Add(object dataItem)
|
||||
{
|
||||
if (this.OwningGrid.SelectionMode == DataGridSelectionMode.Single)
|
||||
{
|
||||
throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode();
|
||||
}
|
||||
|
||||
int itemIndex = this.OwningGrid.DataConnection.IndexOf(dataItem);
|
||||
if (itemIndex == -1)
|
||||
{
|
||||
throw DataGridError.DataGrid.ItemIsNotContainedInTheItemsSource("dataItem");
|
||||
}
|
||||
|
||||
Debug.Assert(itemIndex >= 0, "Expected positive itemIndex.");
|
||||
|
||||
int slot = this.OwningGrid.SlotFromRowIndex(itemIndex);
|
||||
if (_selectedSlotsTable.RangeCount == 0)
|
||||
{
|
||||
this.OwningGrid.SelectedItem = dataItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.OwningGrid.SetRowSelection(slot, true /*isSelected*/, false /*setAnchorSlot*/);
|
||||
}
|
||||
|
||||
return _selectedSlotsTable.IndexOf(slot);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (this.OwningGrid.SelectionMode == DataGridSelectionMode.Single)
|
||||
{
|
||||
throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode();
|
||||
}
|
||||
|
||||
if (_selectedSlotsTable.RangeCount > 0)
|
||||
{
|
||||
// Clearing the selection does not reset the potential current cell.
|
||||
if (!this.OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/))
|
||||
{
|
||||
// Edited value couldn't be committed or aborted
|
||||
return;
|
||||
}
|
||||
|
||||
this.OwningGrid.ClearRowSelection(true /*resetAnchorSlot*/);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(object dataItem)
|
||||
{
|
||||
int itemIndex = this.OwningGrid.DataConnection.IndexOf(dataItem);
|
||||
if (itemIndex == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Assert(itemIndex >= 0, "Expected positive itemIndex.");
|
||||
|
||||
return ContainsSlot(this.OwningGrid.SlotFromRowIndex(itemIndex));
|
||||
}
|
||||
|
||||
public int IndexOf(object dataItem)
|
||||
{
|
||||
int itemIndex = this.OwningGrid.DataConnection.IndexOf(dataItem);
|
||||
if (itemIndex == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
Debug.Assert(itemIndex >= 0, "Expected positive itemIndex.");
|
||||
|
||||
int slot = this.OwningGrid.SlotFromRowIndex(itemIndex);
|
||||
return _selectedSlotsTable.IndexOf(slot);
|
||||
}
|
||||
|
||||
public void Insert(int index, object dataItem)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Remove(object dataItem)
|
||||
{
|
||||
if (this.OwningGrid.SelectionMode == DataGridSelectionMode.Single)
|
||||
{
|
||||
throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode();
|
||||
}
|
||||
|
||||
int itemIndex = this.OwningGrid.DataConnection.IndexOf(dataItem);
|
||||
if (itemIndex == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Assert(itemIndex >= 0, "Expected positive itemIndex.");
|
||||
|
||||
if (itemIndex == this.OwningGrid.CurrentSlot &&
|
||||
!this.OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/))
|
||||
{
|
||||
// Edited value couldn't be committed or aborted
|
||||
return;
|
||||
}
|
||||
|
||||
this.OwningGrid.SetRowSelection(itemIndex, false /*isSelected*/, false /*setAnchorSlot*/);
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (this.OwningGrid.SelectionMode == DataGridSelectionMode.Single)
|
||||
{
|
||||
throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode();
|
||||
}
|
||||
|
||||
if (index < 0 || index >= _selectedSlotsTable.IndexCount)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueMustBeBetween("index", "Index", 0, true, _selectedSlotsTable.IndexCount, false);
|
||||
}
|
||||
|
||||
int rowIndex = _selectedSlotsTable.GetNthIndex(index);
|
||||
Debug.Assert(rowIndex > -1, "Expected positive itemIndex.");
|
||||
|
||||
if (rowIndex == this.OwningGrid.CurrentSlot &&
|
||||
!this.OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/))
|
||||
{
|
||||
// Edited value couldn't be committed or aborted
|
||||
return;
|
||||
}
|
||||
|
||||
this.OwningGrid.SetRowSelection(rowIndex, false /*isSelected*/, false /*setAnchorSlot*/);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return _selectedSlotsTable.IndexCount;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSynchronized
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
// TODO: Not supported yet.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
Debug.Assert(this.OwningGrid != null, "Expected non-null owning DataGrid.");
|
||||
Debug.Assert(this.OwningGrid.DataConnection != null, "Expected non-null owning DataGrid.DataConnection.");
|
||||
Debug.Assert(_selectedSlotsTable != null, "Expected non-null _selectedSlotsTable.");
|
||||
|
||||
foreach (int slot in _selectedSlotsTable.GetIndexes())
|
||||
{
|
||||
int rowIndex = this.OwningGrid.RowIndexFromSlot(slot);
|
||||
Debug.Assert(rowIndex > -1, "Expected positive rowIndex.");
|
||||
yield return this.OwningGrid.DataConnection.GetDataItem(rowIndex);
|
||||
}
|
||||
}
|
||||
|
||||
internal DataGrid OwningGrid
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal List<object> SelectedItemsCache
|
||||
{
|
||||
get
|
||||
{
|
||||
return _selectedItemsCache;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_selectedItemsCache = value;
|
||||
UpdateIndexes();
|
||||
}
|
||||
}
|
||||
|
||||
internal void ClearRows()
|
||||
{
|
||||
_selectedSlotsTable.Clear();
|
||||
_selectedItemsCache.Clear();
|
||||
}
|
||||
|
||||
internal bool ContainsSlot(int slot)
|
||||
{
|
||||
return _selectedSlotsTable.Contains(slot);
|
||||
}
|
||||
|
||||
internal bool ContainsAll(int startSlot, int endSlot)
|
||||
{
|
||||
int itemSlot = this.OwningGrid.RowGroupHeadersTable.GetNextGap(startSlot - 1);
|
||||
while (itemSlot <= endSlot)
|
||||
{
|
||||
// Skip over the RowGroupHeaderSlots
|
||||
int nextRowGroupHeaderSlot = this.OwningGrid.RowGroupHeadersTable.GetNextIndex(itemSlot);
|
||||
int lastItemSlot = nextRowGroupHeaderSlot == -1 ? endSlot : Math.Min(endSlot, nextRowGroupHeaderSlot - 1);
|
||||
if (!_selectedSlotsTable.ContainsAll(itemSlot, lastItemSlot))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
itemSlot = this.OwningGrid.RowGroupHeadersTable.GetNextGap(lastItemSlot);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called when an item is deleted from the ItemsSource as opposed to just being unselected
|
||||
internal void Delete(int slot, object item)
|
||||
{
|
||||
if (_oldSelectedSlotsTable.Contains(slot))
|
||||
{
|
||||
this.OwningGrid.SelectionHasChanged = true;
|
||||
}
|
||||
|
||||
DeleteSlot(slot);
|
||||
_selectedItemsCache.Remove(item);
|
||||
}
|
||||
|
||||
internal void DeleteSlot(int slot)
|
||||
{
|
||||
_selectedSlotsTable.RemoveIndex(slot);
|
||||
_oldSelectedSlotsTable.RemoveIndex(slot);
|
||||
}
|
||||
|
||||
// Returns the inclusive index count between lowerBound and upperBound of all indexes with the given value
|
||||
internal int GetIndexCount(int lowerBound, int upperBound)
|
||||
{
|
||||
return _selectedSlotsTable.GetIndexCount(lowerBound, upperBound, true);
|
||||
}
|
||||
|
||||
internal IEnumerable<int> GetIndexes()
|
||||
{
|
||||
return _selectedSlotsTable.GetIndexes();
|
||||
}
|
||||
|
||||
internal IEnumerable<int> GetSlots(int startSlot)
|
||||
{
|
||||
return _selectedSlotsTable.GetIndexes(startSlot);
|
||||
}
|
||||
|
||||
internal SelectionChangedEventArgs GetSelectionChangedEventArgs()
|
||||
{
|
||||
List<object> addedSelectedItems = new List<object>();
|
||||
List<object> removedSelectedItems = new List<object>();
|
||||
|
||||
// Compare the old selected indexes with the current selection to determine which items
|
||||
// have been added and removed since the last time this method was called
|
||||
foreach (int newSlot in _selectedSlotsTable.GetIndexes())
|
||||
{
|
||||
object newItem = this.OwningGrid.DataConnection.GetDataItem(this.OwningGrid.RowIndexFromSlot(newSlot));
|
||||
if (_oldSelectedSlotsTable.Contains(newSlot))
|
||||
{
|
||||
_oldSelectedSlotsTable.RemoveValue(newSlot);
|
||||
_oldSelectedItemsCache.Remove(newItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
addedSelectedItems.Add(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (object oldItem in _oldSelectedItemsCache)
|
||||
{
|
||||
removedSelectedItems.Add(oldItem);
|
||||
}
|
||||
|
||||
// The current selection becomes the old selection
|
||||
_oldSelectedSlotsTable = _selectedSlotsTable.Copy();
|
||||
_oldSelectedItemsCache = new List<object>(_selectedItemsCache);
|
||||
|
||||
return new SelectionChangedEventArgs(removedSelectedItems, addedSelectedItems);
|
||||
}
|
||||
|
||||
internal void InsertIndex(int slot)
|
||||
{
|
||||
_selectedSlotsTable.InsertIndex(slot);
|
||||
_oldSelectedSlotsTable.InsertIndex(slot);
|
||||
|
||||
// It's possible that we're inserting an item that was just removed. If that's the case,
|
||||
// and the re-inserted item used to be selected, we want to update the _oldSelectedSlotsTable
|
||||
// to include the item's new index within the collection.
|
||||
int rowIndex = this.OwningGrid.RowIndexFromSlot(slot);
|
||||
if (rowIndex != -1)
|
||||
{
|
||||
object insertedItem = this.OwningGrid.DataConnection.GetDataItem(rowIndex);
|
||||
if (insertedItem != null && _oldSelectedItemsCache.Contains(insertedItem))
|
||||
{
|
||||
_oldSelectedSlotsTable.AddValue(slot, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SelectSlot(int slot, bool select)
|
||||
{
|
||||
if (this.OwningGrid.RowGroupHeadersTable.Contains(slot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (select)
|
||||
{
|
||||
if (!_selectedSlotsTable.Contains(slot))
|
||||
{
|
||||
_selectedItemsCache.Add(this.OwningGrid.DataConnection.GetDataItem(this.OwningGrid.RowIndexFromSlot(slot)));
|
||||
}
|
||||
|
||||
_selectedSlotsTable.AddValue(slot, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_selectedSlotsTable.Contains(slot))
|
||||
{
|
||||
_selectedItemsCache.Remove(this.OwningGrid.DataConnection.GetDataItem(this.OwningGrid.RowIndexFromSlot(slot)));
|
||||
}
|
||||
|
||||
_selectedSlotsTable.RemoveValue(slot);
|
||||
}
|
||||
}
|
||||
|
||||
internal void SelectSlots(int startSlot, int endSlot, bool select)
|
||||
{
|
||||
int itemSlot = this.OwningGrid.RowGroupHeadersTable.GetNextGap(startSlot - 1);
|
||||
int endItemSlot = this.OwningGrid.RowGroupHeadersTable.GetPreviousGap(endSlot + 1);
|
||||
|
||||
if (select)
|
||||
{
|
||||
while (itemSlot <= endItemSlot)
|
||||
{
|
||||
// Add the newly selected item slots by skipping over the RowGroupHeaderSlots
|
||||
int nextRowGroupHeaderSlot = this.OwningGrid.RowGroupHeadersTable.GetNextIndex(itemSlot);
|
||||
int lastItemSlot = nextRowGroupHeaderSlot == -1 ? endItemSlot : Math.Min(endItemSlot, nextRowGroupHeaderSlot - 1);
|
||||
|
||||
for (int slot = itemSlot; slot <= lastItemSlot; slot++)
|
||||
{
|
||||
if (!_selectedSlotsTable.Contains(slot))
|
||||
{
|
||||
_selectedItemsCache.Add(this.OwningGrid.DataConnection.GetDataItem(this.OwningGrid.RowIndexFromSlot(slot)));
|
||||
}
|
||||
}
|
||||
|
||||
_selectedSlotsTable.AddValues(itemSlot, lastItemSlot - itemSlot + 1, true);
|
||||
itemSlot = this.OwningGrid.RowGroupHeadersTable.GetNextGap(lastItemSlot);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (itemSlot <= endItemSlot)
|
||||
{
|
||||
// Remove the unselected item slots by skipping over the RowGroupHeaderSlots
|
||||
int nextRowGroupHeaderSlot = this.OwningGrid.RowGroupHeadersTable.GetNextIndex(itemSlot);
|
||||
int lastItemSlot = nextRowGroupHeaderSlot == -1 ? endItemSlot : Math.Min(endItemSlot, nextRowGroupHeaderSlot - 1);
|
||||
|
||||
for (int slot = itemSlot; slot <= lastItemSlot; slot++)
|
||||
{
|
||||
if (_selectedSlotsTable.Contains(slot))
|
||||
{
|
||||
_selectedItemsCache.Remove(this.OwningGrid.DataConnection.GetDataItem(this.OwningGrid.RowIndexFromSlot(slot)));
|
||||
}
|
||||
}
|
||||
|
||||
_selectedSlotsTable.RemoveValues(itemSlot, lastItemSlot - itemSlot + 1);
|
||||
itemSlot = this.OwningGrid.RowGroupHeadersTable.GetNextGap(lastItemSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateIndexes()
|
||||
{
|
||||
_oldSelectedSlotsTable.Clear();
|
||||
_selectedSlotsTable.Clear();
|
||||
|
||||
if (this.OwningGrid.DataConnection.DataSource == null)
|
||||
{
|
||||
if (this.SelectedItemsCache.Count > 0)
|
||||
{
|
||||
this.OwningGrid.SelectionHasChanged = true;
|
||||
this.SelectedItemsCache.Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
List<object> tempSelectedItemsCache = new List<object>();
|
||||
foreach (object item in _selectedItemsCache)
|
||||
{
|
||||
int index = this.OwningGrid.DataConnection.IndexOf(item);
|
||||
if (index != -1)
|
||||
{
|
||||
tempSelectedItemsCache.Add(item);
|
||||
_selectedSlotsTable.AddValue(this.OwningGrid.SlotFromRowIndex(index), true);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (object item in _oldSelectedItemsCache)
|
||||
{
|
||||
int index = this.OwningGrid.DataConnection.IndexOf(item);
|
||||
if (index == -1)
|
||||
{
|
||||
this.OwningGrid.SelectionHasChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_oldSelectedSlotsTable.AddValue(this.OwningGrid.SlotFromRowIndex(index), true);
|
||||
}
|
||||
}
|
||||
|
||||
_selectedItemsCache = tempSelectedItemsCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
// 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.DataGridInternals;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="DataGrid"/> column that hosts template-specified
|
||||
/// content in its cells.
|
||||
/// </summary>
|
||||
public class DataGridTemplateColumn : DataGridColumn
|
||||
{
|
||||
private DataTemplate _cellTemplate;
|
||||
private DataTemplate _cellEditingTemplate;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridTemplateColumn"/> class.
|
||||
/// </summary>
|
||||
public DataGridTemplateColumn()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template that is used to display the contents of a cell that is in editing mode.
|
||||
/// </summary>
|
||||
public DataTemplate CellEditingTemplate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cellEditingTemplate;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_cellEditingTemplate != value)
|
||||
{
|
||||
this.RemoveEditingElement();
|
||||
_cellEditingTemplate = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template that is used to display the contents of a cell that is not in editing mode.
|
||||
/// </summary>
|
||||
public DataTemplate CellTemplate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cellTemplate;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_cellTemplate != value)
|
||||
{
|
||||
if (_cellEditingTemplate == null)
|
||||
{
|
||||
this.RemoveEditingElement();
|
||||
}
|
||||
|
||||
_cellTemplate = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal bool HasDistinctTemplates
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.CellTemplate != this.CellEditingTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CancelCellEdit
|
||||
/// </summary>
|
||||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
||||
/// <param name="uneditedValue">The previous, unedited value in the cell being edited.</param>
|
||||
protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
|
||||
{
|
||||
_ = GenerateEditingElement(null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an element defined by the <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridTemplateColumn.CellEditingTemplate"/> that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.
|
||||
/// </summary>
|
||||
/// <returns>A new editing element that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.</returns>
|
||||
/// <param name="cell">The cell that will contain the generated element.</param>
|
||||
/// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
|
||||
/// <exception cref="T:System.TypeInitializationException">
|
||||
/// The <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridTemplateColumn.CellEditingTemplate"/> is null.
|
||||
/// </exception>
|
||||
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
if (this.CellEditingTemplate != null)
|
||||
{
|
||||
return this.CellEditingTemplate.LoadContent() as FrameworkElement;
|
||||
}
|
||||
|
||||
if (this.CellTemplate != null)
|
||||
{
|
||||
return this.CellTemplate.LoadContent() as FrameworkElement;
|
||||
}
|
||||
|
||||
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw DataGridError.DataGridTemplateColumn.MissingTemplateForType(typeof(DataGridTemplateColumn));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an element defined by the <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridTemplateColumn.CellTemplate"/> that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.
|
||||
/// </summary>
|
||||
/// <returns>A new, read-only element that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.</returns>
|
||||
/// <param name="cell">The cell that will contain the generated element.</param>
|
||||
/// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
|
||||
/// <exception cref="T:System.TypeInitializationException">
|
||||
/// The <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridTemplateColumn.CellTemplate"/> is null.
|
||||
/// </exception>
|
||||
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
if (this.CellTemplate != null)
|
||||
{
|
||||
return this.CellTemplate.LoadContent() as FrameworkElement;
|
||||
}
|
||||
|
||||
if (this.CellEditingTemplate != null)
|
||||
{
|
||||
return this.CellEditingTemplate.LoadContent() as FrameworkElement;
|
||||
}
|
||||
|
||||
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw DataGridError.DataGridTemplateColumn.MissingTemplateForType(typeof(DataGridTemplateColumn));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a cell in the column enters editing mode.
|
||||
/// </summary>
|
||||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
||||
/// <param name="editingEventArgs">Information about the user gesture that is causing a cell to enter editing mode.</param>
|
||||
/// <returns>null in all cases.</returns>
|
||||
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,433 @@
|
|||
// 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 Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Text;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="DataGrid"/> column that hosts textual content in its cells.
|
||||
/// </summary>
|
||||
[StyleTypedProperty(Property = "ElementStyle", StyleTargetType = typeof(TextBlock))]
|
||||
[StyleTypedProperty(Property = "EditingElementStyle", StyleTargetType = typeof(TextBox))]
|
||||
public class DataGridTextColumn : DataGridBoundColumn
|
||||
{
|
||||
private const string DATAGRIDTEXTCOLUMN_fontFamilyName = "FontFamily";
|
||||
private const string DATAGRIDTEXTCOLUMN_fontSizeName = "FontSize";
|
||||
private const string DATAGRIDTEXTCOLUMN_fontStyleName = "FontStyle";
|
||||
private const string DATAGRIDTEXTCOLUMN_fontWeightName = "FontWeight";
|
||||
private const string DATAGRIDTEXTCOLUMN_foregroundName = "Foreground";
|
||||
private const double DATAGRIDTEXTCOLUMN_leftMargin = 12.0;
|
||||
private const double DATAGRIDTEXTCOLUMN_rightMargin = 12.0;
|
||||
|
||||
private double? _fontSize;
|
||||
private FontStyle? _fontStyle;
|
||||
private FontWeight? _fontWeight;
|
||||
private Brush _foreground;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataGridTextColumn"/> class.
|
||||
/// </summary>
|
||||
public DataGridTextColumn()
|
||||
{
|
||||
this.BindingTarget = TextBox.TextProperty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font name.
|
||||
/// </summary>
|
||||
public FontFamily FontFamily
|
||||
{
|
||||
get { return (FontFamily)GetValue(FontFamilyProperty); }
|
||||
set { SetValue(FontFamilyProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the FontFamily dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FontFamilyProperty =
|
||||
DependencyProperty.Register(
|
||||
DATAGRIDTEXTCOLUMN_fontFamilyName,
|
||||
typeof(FontFamily),
|
||||
typeof(DataGridTextColumn),
|
||||
new PropertyMetadata(null, OnFontFamilyPropertyChanged));
|
||||
|
||||
private static void OnFontFamilyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
DataGridTextColumn textColumn = d as DataGridTextColumn;
|
||||
textColumn.NotifyPropertyChanged(DATAGRIDTEXTCOLUMN_fontFamilyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font size.
|
||||
/// </summary>
|
||||
// Use DefaultValue here so undo in the Designer will set this to NaN
|
||||
[DefaultValue(double.NaN)]
|
||||
public double FontSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fontSize ?? double.NaN;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_fontSize != value)
|
||||
{
|
||||
_fontSize = value;
|
||||
NotifyPropertyChanged(DATAGRIDTEXTCOLUMN_fontSizeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font style.
|
||||
/// </summary>
|
||||
public FontStyle FontStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fontStyle ?? FontStyle.Normal;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_fontStyle != value)
|
||||
{
|
||||
_fontStyle = value;
|
||||
NotifyPropertyChanged(DATAGRIDTEXTCOLUMN_fontStyleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font weight or thickness.
|
||||
/// </summary>
|
||||
public FontWeight FontWeight
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fontWeight ?? FontWeights.Normal;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!_fontWeight.HasValue || _fontWeight.Value.Weight != value.Weight)
|
||||
{
|
||||
_fontWeight = value;
|
||||
NotifyPropertyChanged(DATAGRIDTEXTCOLUMN_fontWeightName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a brush that describes the foreground of the column cells.
|
||||
/// </summary>
|
||||
public Brush Foreground
|
||||
{
|
||||
get
|
||||
{
|
||||
return _foreground;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_foreground != value)
|
||||
{
|
||||
_foreground = value;
|
||||
NotifyPropertyChanged(DATAGRIDTEXTCOLUMN_foregroundName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Causes the column cell being edited to revert to the specified value.
|
||||
/// </summary>
|
||||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
||||
/// <param name="uneditedValue">The previous, unedited value in the cell being edited.</param>
|
||||
protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
|
||||
{
|
||||
TextBox textBox = editingElement as TextBox;
|
||||
if (textBox != null)
|
||||
{
|
||||
string uneditedString = uneditedValue as string;
|
||||
textBox.Text = uneditedString ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="T:System.Windows.Controls.TextBox"/> control that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.
|
||||
/// </summary>
|
||||
/// <param name="cell">The cell that will contain the generated element.</param>
|
||||
/// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
|
||||
/// <returns>A new <see cref="T:System.Windows.Controls.TextBox"/> control that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.</returns>
|
||||
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
TextBox textBox = new TextBox();
|
||||
textBox.VerticalAlignment = VerticalAlignment.Stretch;
|
||||
textBox.Background = new SolidColorBrush(Colors.Transparent);
|
||||
|
||||
if (DependencyProperty.UnsetValue != ReadLocalValue(DataGridTextColumn.FontFamilyProperty))
|
||||
{
|
||||
textBox.FontFamily = this.FontFamily;
|
||||
}
|
||||
|
||||
if (_fontSize.HasValue)
|
||||
{
|
||||
textBox.FontSize = _fontSize.Value;
|
||||
}
|
||||
|
||||
if (_fontStyle.HasValue)
|
||||
{
|
||||
textBox.FontStyle = _fontStyle.Value;
|
||||
}
|
||||
|
||||
if (_fontWeight.HasValue)
|
||||
{
|
||||
textBox.FontWeight = _fontWeight.Value;
|
||||
}
|
||||
|
||||
RefreshForeground(textBox, (cell != null & cell.OwningRow != null) ? cell.OwningRow.ComputedForeground : null);
|
||||
|
||||
if (this.Binding != null)
|
||||
{
|
||||
textBox.SetBinding(this.BindingTarget, this.Binding);
|
||||
}
|
||||
|
||||
return textBox;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only <see cref="T:System.Windows.Controls.TextBlock"/> element that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.
|
||||
/// </summary>
|
||||
/// <param name="cell">The cell that will contain the generated element.</param>
|
||||
/// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
|
||||
/// <returns>A new, read-only <see cref="T:System.Windows.Controls.TextBlock"/> element that is bound to the column's <see cref="P:Microsoft.Toolkit.Uwp.UI.Controls.DataGridBoundColumn.Binding"/> property value.</returns>
|
||||
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
|
||||
{
|
||||
TextBlock textBlockElement = new TextBlock();
|
||||
textBlockElement.Margin = new Thickness(DATAGRIDTEXTCOLUMN_leftMargin, 0.0, DATAGRIDTEXTCOLUMN_rightMargin, 0.0);
|
||||
textBlockElement.VerticalAlignment = VerticalAlignment.Center;
|
||||
if (DependencyProperty.UnsetValue != ReadLocalValue(DataGridTextColumn.FontFamilyProperty))
|
||||
{
|
||||
textBlockElement.FontFamily = this.FontFamily;
|
||||
}
|
||||
|
||||
if (_fontSize.HasValue)
|
||||
{
|
||||
textBlockElement.FontSize = _fontSize.Value;
|
||||
}
|
||||
|
||||
if (_fontStyle.HasValue)
|
||||
{
|
||||
textBlockElement.FontStyle = _fontStyle.Value;
|
||||
}
|
||||
|
||||
if (_fontWeight.HasValue)
|
||||
{
|
||||
textBlockElement.FontWeight = _fontWeight.Value;
|
||||
}
|
||||
|
||||
RefreshForeground(textBlockElement, (cell != null & cell.OwningRow != null) ? cell.OwningRow.ComputedForeground : null);
|
||||
|
||||
if (this.Binding != null)
|
||||
{
|
||||
textBlockElement.SetBinding(TextBlock.TextProperty, this.Binding);
|
||||
}
|
||||
|
||||
return textBlockElement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the cell in the column enters editing mode.
|
||||
/// </summary>
|
||||
/// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
|
||||
/// <param name="editingEventArgs">Information about the user gesture that is causing a cell to enter editing mode.</param>
|
||||
/// <returns>The unedited value. </returns>
|
||||
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
|
||||
{
|
||||
TextBox textBox = editingElement as TextBox;
|
||||
if (textBox != null)
|
||||
{
|
||||
string uneditedText = textBox.Text;
|
||||
int len = uneditedText.Length;
|
||||
KeyRoutedEventArgs keyEventArgs = editingEventArgs as KeyRoutedEventArgs;
|
||||
if (keyEventArgs != null && keyEventArgs.Key == Windows.System.VirtualKey.F2)
|
||||
{
|
||||
// Put caret at the end of the text
|
||||
textBox.Select(len, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select all text
|
||||
textBox.Select(0, len);
|
||||
}
|
||||
|
||||
return uneditedText;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the DataGrid control when this column asks for its elements to be updated, because a property changed.
|
||||
/// </summary>
|
||||
protected internal override void RefreshCellContent(FrameworkElement element, Brush computedRowForeground, string propertyName)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException("element");
|
||||
}
|
||||
|
||||
TextBox textBox = element as TextBox;
|
||||
if (textBox == null)
|
||||
{
|
||||
TextBlock textBlock = element as TextBlock;
|
||||
if (textBlock == null)
|
||||
{
|
||||
throw DataGridError.DataGrid.ValueIsNotAnInstanceOfEitherOr("element", typeof(TextBox), typeof(TextBlock));
|
||||
}
|
||||
|
||||
if (propertyName == DATAGRIDTEXTCOLUMN_fontFamilyName)
|
||||
{
|
||||
textBlock.FontFamily = this.FontFamily;
|
||||
}
|
||||
else if (propertyName == DATAGRIDTEXTCOLUMN_fontSizeName)
|
||||
{
|
||||
SetTextFontSize(textBlock, TextBlock.FontSizeProperty);
|
||||
}
|
||||
else if (propertyName == DATAGRIDTEXTCOLUMN_fontStyleName)
|
||||
{
|
||||
textBlock.FontStyle = this.FontStyle;
|
||||
}
|
||||
else if (propertyName == DATAGRIDTEXTCOLUMN_fontWeightName)
|
||||
{
|
||||
textBlock.FontWeight = this.FontWeight;
|
||||
}
|
||||
else if (propertyName == DATAGRIDTEXTCOLUMN_foregroundName)
|
||||
{
|
||||
RefreshForeground(textBlock, computedRowForeground);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.FontFamily != null)
|
||||
{
|
||||
textBlock.FontFamily = this.FontFamily;
|
||||
}
|
||||
|
||||
SetTextFontSize(textBlock, TextBlock.FontSizeProperty);
|
||||
textBlock.FontStyle = this.FontStyle;
|
||||
textBlock.FontWeight = this.FontWeight;
|
||||
RefreshForeground(textBlock, computedRowForeground);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (propertyName == DATAGRIDTEXTCOLUMN_fontFamilyName)
|
||||
{
|
||||
textBox.FontFamily = this.FontFamily;
|
||||
}
|
||||
else if (propertyName == DATAGRIDTEXTCOLUMN_fontSizeName)
|
||||
{
|
||||
SetTextFontSize(textBox, TextBox.FontSizeProperty);
|
||||
}
|
||||
else if (propertyName == DATAGRIDTEXTCOLUMN_fontStyleName)
|
||||
{
|
||||
textBox.FontStyle = this.FontStyle;
|
||||
}
|
||||
else if (propertyName == DATAGRIDTEXTCOLUMN_fontWeightName)
|
||||
{
|
||||
textBox.FontWeight = this.FontWeight;
|
||||
}
|
||||
else if (propertyName == DATAGRIDTEXTCOLUMN_foregroundName)
|
||||
{
|
||||
RefreshForeground(textBox, computedRowForeground);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.FontFamily != null)
|
||||
{
|
||||
textBox.FontFamily = this.FontFamily;
|
||||
}
|
||||
|
||||
SetTextFontSize(textBox, TextBox.FontSizeProperty);
|
||||
textBox.FontStyle = this.FontStyle;
|
||||
textBox.FontWeight = this.FontWeight;
|
||||
RefreshForeground(textBox, computedRowForeground);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the computed foreground of a row changed.
|
||||
/// </summary>
|
||||
protected internal override void RefreshForeground(FrameworkElement element, Brush computedRowForeground)
|
||||
{
|
||||
TextBox textBox = element as TextBox;
|
||||
if (textBox != null)
|
||||
{
|
||||
RefreshForeground(textBox, computedRowForeground);
|
||||
}
|
||||
else
|
||||
{
|
||||
TextBlock textBlock = element as TextBlock;
|
||||
if (textBlock != null)
|
||||
{
|
||||
RefreshForeground(textBlock, computedRowForeground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshForeground(TextBlock textBlock, Brush computedRowForeground)
|
||||
{
|
||||
if (this.Foreground == null)
|
||||
{
|
||||
if (computedRowForeground != null)
|
||||
{
|
||||
textBlock.Foreground = computedRowForeground;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
textBlock.Foreground = this.Foreground;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshForeground(TextBox textBox, Brush computedRowForeground)
|
||||
{
|
||||
if (this.Foreground == null)
|
||||
{
|
||||
if (computedRowForeground != null)
|
||||
{
|
||||
textBox.Foreground = computedRowForeground;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
textBox.Foreground = this.Foreground;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTextFontSize(DependencyObject textElement, DependencyProperty fontSizeProperty)
|
||||
{
|
||||
double newFontSize = this.FontSize;
|
||||
if (double.IsNaN(newFontSize))
|
||||
{
|
||||
textElement.ClearValue(fontSizeProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
textElement.SetValue(fontSizeProperty, newFontSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// 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 Microsoft.Toolkit.Uwp.Utilities;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.DataGridInternals
|
||||
{
|
||||
internal class DataGridValueConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (targetType != null && TypeHelper.IsNullableType(targetType))
|
||||
{
|
||||
string strValue = value as string;
|
||||
if (strValue == string.Empty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<Project Sdk="MSBuild.Sdk.Extras">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>uap10.0.17763</TargetFramework>
|
||||
<Title>Windows Community Toolkit Controls DataGrid</Title>
|
||||
<Description>
|
||||
This library provides a XAML DataGrid control. It is part of the Windows Community Toolkit.
|
||||
|
||||
</Description>
|
||||
<PackageTags>UWP Toolkit Windows Controls XAML DataGrid</PackageTags>
|
||||
<RootNamespace>Microsoft.Toolkit.Uwp.UI.Controls</RootNamespace>
|
||||
<LangVersion>preview</LangVersion> <!-- Fix a small internal bug -->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="VisualStudioToolsManifest.xml" Pack="true" PackagePath="tools" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,3 @@
|
|||
<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/=datagrid/@EntryIndexedValue">True</s:Boolean>
|
||||
</wpf:ResourceDictionary>
|
|
@ -0,0 +1,13 @@
|
|||
// 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.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
// TODO: Fix tests for WinUI3
|
||||
// [assembly: InternalsVisibleTo("UnitTests.UWP")]
|
||||
[assembly: NeutralResourcesLanguage("en-US")]
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Library Name="Microsoft.Toolkit.Uwp.UI.Controls.DataGrid">
|
||||
</Library>
|
||||
</Directives>
|
108
Tests/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/Properties/Resources.Designer.cs
сгенерированный
Normal file
108
Tests/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/Properties/Resources.Designer.cs
сгенерированный
Normal file
|
@ -0,0 +1,108 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Toolkit.Uwp.UI.Controls.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to row.
|
||||
/// </summary>
|
||||
internal static string DataGridRowAutomationPeer_ItemType {
|
||||
get {
|
||||
return ResourceManager.GetString("DataGridRowAutomationPeer_ItemType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ({0} items).
|
||||
/// </summary>
|
||||
internal static string DataGridRowGroupHeader_ItemCountPlural {
|
||||
get {
|
||||
return ResourceManager.GetString("DataGridRowGroupHeader_ItemCountPlural", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ({0} item).
|
||||
/// </summary>
|
||||
internal static string DataGridRowGroupHeader_ItemCountSingular {
|
||||
get {
|
||||
return ResourceManager.GetString("DataGridRowGroupHeader_ItemCountSingular", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0}:.
|
||||
/// </summary>
|
||||
internal static string DataGridRowGroupHeader_PropertyName {
|
||||
get {
|
||||
return ResourceManager.GetString("DataGridRowGroupHeader_PropertyName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Group.
|
||||
/// </summary>
|
||||
internal static string DefaultRowGroupHeaderPropertyNameAlternative {
|
||||
get {
|
||||
return ResourceManager.GetString("DefaultRowGroupHeaderPropertyNameAlternative", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="DataGridRowAutomationPeer_ItemType" xml:space="preserve">
|
||||
<value>row</value>
|
||||
</data>
|
||||
<data name="DataGridRowGroupHeader_ItemCountPlural" xml:space="preserve">
|
||||
<value>({0} items)</value>
|
||||
</data>
|
||||
<data name="DataGridRowGroupHeader_ItemCountSingular" xml:space="preserve">
|
||||
<value>({0} item)</value>
|
||||
</data>
|
||||
<data name="DataGridRowGroupHeader_PropertyName" xml:space="preserve">
|
||||
<value>{0}:</value>
|
||||
</data>
|
||||
<data name="DefaultRowGroupHeaderPropertyNameAlternative" xml:space="preserve">
|
||||
<value>Group</value>
|
||||
</data>
|
||||
</root>
|
|
@ -0,0 +1,18 @@
|
|||
<!--
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
-->
|
||||
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGrid.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
|
@ -0,0 +1,51 @@
|
|||
// 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.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Data.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores information about a Binding, including the BindingExpression, BindingTarget and associated Element.
|
||||
/// </summary>
|
||||
internal class BindingInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BindingInfo"/> class.
|
||||
/// </summary>
|
||||
public BindingInfo()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BindingInfo"/> class
|
||||
/// with the specified BindingExpression, DependencyProperty and FrameworkElement.
|
||||
/// </summary>
|
||||
/// <param name="bindingExpression">BindingExpression</param>
|
||||
/// <param name="bindingTarget">BindingTarget</param>
|
||||
/// <param name="element">Element</param>
|
||||
public BindingInfo(BindingExpression bindingExpression, DependencyProperty bindingTarget, FrameworkElement element)
|
||||
{
|
||||
this.BindingExpression = bindingExpression;
|
||||
this.BindingTarget = bindingTarget;
|
||||
this.Element = element;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the BindingExpression.
|
||||
/// </summary>
|
||||
public BindingExpression BindingExpression { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the BindingTarget.
|
||||
/// </summary>
|
||||
public DependencyProperty BindingTarget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Element.
|
||||
/// </summary>
|
||||
public FrameworkElement Element { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// 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.Utilities
|
||||
{
|
||||
internal static class DoubleUtil
|
||||
{
|
||||
internal const double DBL_EPSILON = 1.1102230246251567e-016;
|
||||
|
||||
/// <summary>
|
||||
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
|
||||
/// not they are within epsilon of each other. Note that this epsilon is proportional
|
||||
/// to the numbers themselves to that AreClose survives scalar multiplication.
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
||||
/// returns false. This is important enough to repeat:
|
||||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
||||
/// used for optimizations *only*.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - the result of the AreClose comparison.
|
||||
/// </returns>
|
||||
/// <param name="value1">The first double to compare.</param>
|
||||
/// <param name="value2">The second double to compare.</param>
|
||||
public static bool AreClose(double value1, double value2)
|
||||
{
|
||||
// in case they are Infinities (then epsilon check does not work)
|
||||
if (value1 == value2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON
|
||||
double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON;
|
||||
double delta = value1 - value2;
|
||||
return -eps < delta && eps > delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GreaterThan - Returns whether or not the first double is greater than the second double.
|
||||
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
|
||||
/// the other number. Note that this epsilon is proportional to the numbers themselves
|
||||
/// to that AreClose survives scalar multiplication.
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
||||
/// returns false. This is important enough to repeat:
|
||||
/// NB: This method should be used for optimizations only.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - the result of the GreaterThan comparison.
|
||||
/// </returns>
|
||||
/// <param name="value1">The first double to compare.</param>
|
||||
/// <param name="value2">The second double to compare.</param>
|
||||
public static bool GreaterThan(double value1, double value2)
|
||||
{
|
||||
return value1 > value2 && !AreClose(value1, value2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GreaterThanOrClose - Returns whether or not the first double is greater than or close to
|
||||
/// the second double. That is, whether or not the first is strictly greater than or within
|
||||
/// epsilon of the other number. Note that this epsilon is proportional to the numbers
|
||||
/// themselves to that AreClose survives scalar multiplication.
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
||||
/// returns false. This is important enough to repeat:
|
||||
/// NB: This method should be used for optimizations only.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - the result of the GreaterThanOrClose comparison.
|
||||
/// </returns>
|
||||
/// <param name="value1">The first double to compare.</param>
|
||||
/// <param name="value2">The second double to compare.</param>
|
||||
public static bool GreaterThanOrClose(double value1, double value2)
|
||||
{
|
||||
return value1 > value2 || AreClose(value1, value2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0),
|
||||
/// but this is faster.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - the result of the IsZero comparison.
|
||||
/// </returns>
|
||||
/// <param name="value">The double to compare to 0.</param>
|
||||
public static bool IsZero(double value)
|
||||
{
|
||||
return Math.Abs(value) < 10.0 * DBL_EPSILON;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LessThan - Returns whether or not the first double is less than the second double.
|
||||
/// That is, whether or not the first is strictly less than *and* not within epsilon of
|
||||
/// the other number. Note that this epsilon is proportional to the numbers themselves
|
||||
/// to that AreClose survives scalar multiplication.
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
||||
/// returns false. This is important enough to repeat:
|
||||
/// NB: This method should be used for optimizations only.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - the result of the LessThan comparison.
|
||||
/// </returns>
|
||||
/// <param name="value1">The first double to compare.</param>
|
||||
/// <param name="value2">The second double to compare.</param>
|
||||
public static bool LessThan(double value1, double value2)
|
||||
{
|
||||
return value1 < value2 && !AreClose(value1, value2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LessThanOrClose - Returns whether or not the first double is less than or close to
|
||||
/// the second double. That is, whether or not the first is strictly less than or within
|
||||
/// epsilon of the other number. Note that this epsilon is proportional to the numbers
|
||||
/// themselves to that AreClose survives scalar multiplication.
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
||||
/// returns false. This is important enough to repeat:
|
||||
/// NB: This method should be used for optimizations only.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// bool - the result of the LessThanOrClose comparison.
|
||||
/// </returns>
|
||||
/// <param name="value1">The first double to compare.</param>
|
||||
/// <param name="value2">The second double to compare.</param>
|
||||
public static bool LessThanOrClose(double value1, double value2)
|
||||
{
|
||||
return value1 < value2 || AreClose(value1, value2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Toolkit.Uwp.Utilities;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Utilities
|
||||
{
|
||||
internal static class Extensions
|
||||
{
|
||||
private static Dictionary<DependencyObject, Dictionary<DependencyProperty, int>> _suspendedHandlers = new Dictionary<DependencyObject, Dictionary<DependencyProperty, int>>();
|
||||
|
||||
public static bool IsHandlerSuspended(this DependencyObject dependencyObject, DependencyProperty dependencyProperty)
|
||||
{
|
||||
return _suspendedHandlers.ContainsKey(dependencyObject) ? _suspendedHandlers[dependencyObject].ContainsKey(dependencyProperty) : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Walks the visual tree to determine if a particular child is contained within a parent DependencyObject.
|
||||
/// </summary>
|
||||
/// <param name="element">Parent DependencyObject</param>
|
||||
/// <param name="child">Child DependencyObject</param>
|
||||
/// <returns>True if the parent element contains the child</returns>
|
||||
internal static bool ContainsChild(this DependencyObject element, DependencyObject child)
|
||||
{
|
||||
if (element != null)
|
||||
{
|
||||
while (child != null)
|
||||
{
|
||||
if (child == element)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Walk up the visual tree. If the root is hit, try using the framework element's
|
||||
// parent. This is done because Popups behave differently with respect to the visual tree,
|
||||
// and it could have a parent even if the VisualTreeHelper doesn't find it.
|
||||
DependencyObject parent = VisualTreeHelper.GetParent(child);
|
||||
if (parent == null)
|
||||
{
|
||||
FrameworkElement childElement = child as FrameworkElement;
|
||||
if (childElement != null)
|
||||
{
|
||||
parent = childElement.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
child = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Walks the visual tree to determine if the currently focused element is contained within
|
||||
/// a parent DependencyObject. The FocusManager's GetFocusedElement method is used to determine
|
||||
/// the currently focused element, which is updated synchronously.
|
||||
/// </summary>
|
||||
/// <param name="element">Parent DependencyObject</param>
|
||||
/// <param name="uiElement">Parent UIElement. Used to query the element's XamlRoot.</param>
|
||||
/// <returns>True if the currently focused element is within the visual tree of the parent</returns>
|
||||
internal static bool ContainsFocusedElement(this DependencyObject element, UIElement uiElement)
|
||||
{
|
||||
return (element == null) ? false : element.ContainsChild(GetFocusedElement(uiElement) as DependencyObject);
|
||||
}
|
||||
|
||||
private static object GetFocusedElement(UIElement uiElement)
|
||||
{
|
||||
if (TypeHelper.IsXamlRootAvailable && uiElement.XamlRoot != null)
|
||||
{
|
||||
return FocusManager.GetFocusedElement(uiElement.XamlRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FocusManager.GetFocusedElement();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks a MemberInfo object (e.g. a Type or PropertyInfo) for the ReadOnly attribute
|
||||
/// and returns the value of IsReadOnly if it exists.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo">MemberInfo to check</param>
|
||||
/// <returns>true if MemberInfo is read-only, false otherwise</returns>
|
||||
internal static bool GetIsReadOnly(this MemberInfo memberInfo)
|
||||
{
|
||||
if (memberInfo != null)
|
||||
{
|
||||
// Check if ReadOnlyAttribute is defined on the member
|
||||
object[] attributes = memberInfo.GetCustomAttributes(typeof(ReadOnlyAttribute), true);
|
||||
if (attributes != null && attributes.Length > 0)
|
||||
{
|
||||
ReadOnlyAttribute readOnlyAttribute = attributes[0] as ReadOnlyAttribute;
|
||||
Debug.Assert(readOnlyAttribute != null, "Expected non-null readOnlyAttribute.");
|
||||
return readOnlyAttribute.IsReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static Type GetItemType(this IEnumerable list)
|
||||
{
|
||||
Type listType = list.GetType();
|
||||
Type itemType = null;
|
||||
bool isICustomTypeProvider = false;
|
||||
|
||||
// If it's a generic enumerable, get the generic type.
|
||||
|
||||
// Unfortunately, if data source is fed from a bare IEnumerable, TypeHelper will report an element type of object,
|
||||
// which is not particularly interesting. It is dealt with it further on.
|
||||
if (listType.IsEnumerableType())
|
||||
{
|
||||
itemType = listType.GetEnumerableItemType();
|
||||
if (itemType != null)
|
||||
{
|
||||
isICustomTypeProvider = typeof(ICustomTypeProvider).IsAssignableFrom(itemType);
|
||||
}
|
||||
}
|
||||
|
||||
// Bare IEnumerables mean that result type will be object. In that case, try to get something more interesting.
|
||||
// Or, if the itemType implements ICustomTypeProvider, try to retrieve the custom type from one of the object instances.
|
||||
if (itemType == null || itemType == typeof(object) || isICustomTypeProvider)
|
||||
{
|
||||
// No type was located yet. Does the list have anything in it?
|
||||
Type firstItemType = null;
|
||||
IEnumerator en = list.GetEnumerator();
|
||||
if (en.MoveNext() && en.Current != null)
|
||||
{
|
||||
firstItemType = en.Current.GetCustomOrCLRType();
|
||||
}
|
||||
else
|
||||
{
|
||||
firstItemType = list
|
||||
.Cast<object>() // cast to convert IEnumerable to IEnumerable<object>
|
||||
.Select(x => x.GetType()) // get the type
|
||||
.FirstOrDefault(); // get only the first thing to come out of the sequence, or null if empty
|
||||
}
|
||||
|
||||
if (firstItemType != typeof(object))
|
||||
{
|
||||
return firstItemType;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't get the CustomType because there were no items.
|
||||
if (isICustomTypeProvider)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return itemType;
|
||||
}
|
||||
|
||||
public static void SetStyleWithType(this FrameworkElement element, Style style)
|
||||
{
|
||||
if (element.Style != style && (style == null || style.TargetType != null))
|
||||
{
|
||||
element.Style = style;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetValueNoCallback(this DependencyObject obj, DependencyProperty property, object value)
|
||||
{
|
||||
obj.SuspendHandler(property, true);
|
||||
try
|
||||
{
|
||||
obj.SetValue(property, value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
obj.SuspendHandler(property, false);
|
||||
}
|
||||
}
|
||||
|
||||
internal static Point Translate(this UIElement fromElement, UIElement toElement, Point fromPoint)
|
||||
{
|
||||
if (fromElement == toElement)
|
||||
{
|
||||
return fromPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
return fromElement.TransformToVisual(toElement).TransformPoint(fromPoint);
|
||||
}
|
||||
}
|
||||
|
||||
// If the parent element goes into a background tab, the elements need to be remeasured
|
||||
// or they will report 0 height.
|
||||
internal static UIElement EnsureMeasured(this UIElement element)
|
||||
{
|
||||
if (element.DesiredSize.Height == 0)
|
||||
{
|
||||
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private static void SuspendHandler(this DependencyObject obj, DependencyProperty dependencyProperty, bool incrementSuspensionCount)
|
||||
{
|
||||
if (_suspendedHandlers.ContainsKey(obj))
|
||||
{
|
||||
Dictionary<DependencyProperty, int> suspensions = _suspendedHandlers[obj];
|
||||
|
||||
if (incrementSuspensionCount)
|
||||
{
|
||||
if (suspensions.ContainsKey(dependencyProperty))
|
||||
{
|
||||
suspensions[dependencyProperty]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
suspensions[dependencyProperty] = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(suspensions.ContainsKey(dependencyProperty), "Expected existing key for dependencyProperty.");
|
||||
if (suspensions[dependencyProperty] == 1)
|
||||
{
|
||||
suspensions.Remove(dependencyProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
suspensions[dependencyProperty]--;
|
||||
}
|
||||
|
||||
if (suspensions.Count == 0)
|
||||
{
|
||||
_suspendedHandlers.Remove(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(incrementSuspensionCount, "Expected incrementSuspensionCount==true.");
|
||||
_suspendedHandlers[obj] = new Dictionary<DependencyProperty, int>();
|
||||
_suspendedHandlers[obj][dependencyProperty] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,907 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Utilities
|
||||
{
|
||||
internal class IndexToValueTable<T> : IEnumerable<Range<T>>
|
||||
{
|
||||
private List<Range<T>> _list;
|
||||
|
||||
public IndexToValueTable()
|
||||
{
|
||||
_list = new List<Range<T>>();
|
||||
}
|
||||
|
||||
// Begin Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of indices represented in the table
|
||||
/// </summary>
|
||||
public int IndexCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int indexCount = 0;
|
||||
foreach (Range<T> range in _list)
|
||||
{
|
||||
indexCount += range.Count;
|
||||
}
|
||||
|
||||
return indexCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the table is empty
|
||||
/// </summary>
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return _list.Count == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of index ranges in the table
|
||||
/// </summary>
|
||||
public int RangeCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _list.Count;
|
||||
}
|
||||
}
|
||||
|
||||
// End Public Properties
|
||||
|
||||
// Begin Public methods
|
||||
|
||||
/// <summary>
|
||||
/// Add a value with an associated index to the table
|
||||
/// </summary>
|
||||
/// <param name="index">Index where the value is to be added or updated</param>
|
||||
/// <param name="value">Value to add</param>
|
||||
public void AddValue(int index, T value)
|
||||
{
|
||||
AddValues(index, 1, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add multiples values with an associated start index to the table
|
||||
/// </summary>
|
||||
/// <param name="startIndex">index where first value is added</param>
|
||||
/// <param name="count">Total number of values to add (must be greater than 0)</param>
|
||||
/// <param name="value">Value to add</param>
|
||||
public void AddValues(int startIndex, int count, T value)
|
||||
{
|
||||
Debug.Assert(count > 0, "Expected a strictly positive count parameter.");
|
||||
|
||||
AddValuesPrivate(startIndex, count, value, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the index table
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_list.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given index is contained in the table
|
||||
/// </summary>
|
||||
/// <param name="index">index to search for</param>
|
||||
/// <returns>True if the index is contained in the table</returns>
|
||||
public bool Contains(int index)
|
||||
{
|
||||
return IsCorrectRangeIndex(this.FindRangeIndex(index), index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the entire given index range is contained in the table
|
||||
/// </summary>
|
||||
/// <param name="startIndex">beginning of the range</param>
|
||||
/// <param name="endIndex">end of the range</param>
|
||||
/// <returns>True if the entire index range is present in the table</returns>
|
||||
public bool ContainsAll(int startIndex, int endIndex)
|
||||
{
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
|
||||
foreach (Range<T> range in _list)
|
||||
{
|
||||
if (start == -1 && range.UpperBound >= startIndex)
|
||||
{
|
||||
if (startIndex < range.LowerBound)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
start = startIndex;
|
||||
end = range.UpperBound;
|
||||
if (end >= endIndex)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (start != -1)
|
||||
{
|
||||
if (range.LowerBound > end + 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
end = range.UpperBound;
|
||||
if (end >= endIndex)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given index is contained in the table with the the given value
|
||||
/// </summary>
|
||||
/// <param name="index">index to search for</param>
|
||||
/// <param name="value">value expected</param>
|
||||
/// <returns>true if the given index is contained in the table with the given value</returns>
|
||||
public bool ContainsIndexAndValue(int index, T value)
|
||||
{
|
||||
int lowerRangeIndex = this.FindRangeIndex(index);
|
||||
return IsCorrectRangeIndex(lowerRangeIndex, index) && _list[lowerRangeIndex].ContainsValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of this IndexToValueTable
|
||||
/// </summary>
|
||||
/// <returns>copy of this IndexToValueTable</returns>
|
||||
public IndexToValueTable<T> Copy()
|
||||
{
|
||||
IndexToValueTable<T> copy = new IndexToValueTable<T>();
|
||||
foreach (Range<T> range in _list)
|
||||
{
|
||||
copy._list.Add(range.Copy());
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int GetNextGap(int index)
|
||||
{
|
||||
int targetIndex = index + 1;
|
||||
int rangeIndex = FindRangeIndex(targetIndex);
|
||||
if (IsCorrectRangeIndex(rangeIndex, targetIndex))
|
||||
{
|
||||
while (rangeIndex < _list.Count - 1 && _list[rangeIndex].UpperBound == _list[rangeIndex + 1].LowerBound - 1)
|
||||
{
|
||||
rangeIndex++;
|
||||
}
|
||||
|
||||
return _list[rangeIndex].UpperBound + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return targetIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetNextIndex(int index)
|
||||
{
|
||||
int targetIndex = index + 1;
|
||||
int rangeIndex = FindRangeIndex(targetIndex);
|
||||
if (IsCorrectRangeIndex(rangeIndex, targetIndex))
|
||||
{
|
||||
return targetIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
rangeIndex++;
|
||||
return rangeIndex < _list.Count ? _list[rangeIndex].LowerBound : -1;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetPreviousGap(int index)
|
||||
{
|
||||
int targetIndex = index - 1;
|
||||
int rangeIndex = FindRangeIndex(targetIndex);
|
||||
if (IsCorrectRangeIndex(rangeIndex, targetIndex))
|
||||
{
|
||||
while (rangeIndex > 0 && _list[rangeIndex].LowerBound == _list[rangeIndex - 1].UpperBound + 1)
|
||||
{
|
||||
rangeIndex--;
|
||||
}
|
||||
|
||||
return _list[rangeIndex].LowerBound - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return targetIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetPreviousIndex(int index)
|
||||
{
|
||||
int targetIndex = index - 1;
|
||||
int rangeIndex = FindRangeIndex(targetIndex);
|
||||
if (IsCorrectRangeIndex(rangeIndex, targetIndex))
|
||||
{
|
||||
return targetIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return rangeIndex >= 0 && rangeIndex < _list.Count ? _list[rangeIndex].UpperBound : -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inclusive index count between lowerBound and upperBound of all indexes with the given value
|
||||
/// </summary>
|
||||
/// <param name="lowerBound">lowerBound criteria</param>
|
||||
/// <param name="upperBound">upperBound criteria</param>
|
||||
/// <param name="value">value to look for</param>
|
||||
/// <returns>Number of indexes contained in the table between lowerBound and upperBound (inclusive)</returns>
|
||||
public int GetIndexCount(int lowerBound, int upperBound, T value)
|
||||
{
|
||||
Debug.Assert(upperBound >= lowerBound, "Expected upperBound greater or equal to lowerBound.");
|
||||
|
||||
if (_list.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int index = FindRangeIndex(lowerBound);
|
||||
if (IsCorrectRangeIndex(index, lowerBound) && _list[index].ContainsValue(value))
|
||||
{
|
||||
count += _list[index].UpperBound - lowerBound + 1;
|
||||
}
|
||||
|
||||
index++;
|
||||
while (index < _list.Count && _list[index].UpperBound <= upperBound)
|
||||
{
|
||||
if (_list[index].ContainsValue(value))
|
||||
{
|
||||
count += _list[index].Count;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index < _list.Count && IsCorrectRangeIndex(index, upperBound) && _list[index].ContainsValue(value))
|
||||
{
|
||||
count += upperBound - _list[index].LowerBound;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the inclusive index count between lowerBound and upperBound
|
||||
/// </summary>
|
||||
/// <param name="lowerBound">lowerBound criteria</param>
|
||||
/// <param name="upperBound">upperBound criteria</param>
|
||||
/// <returns>Number of indexes contained in the table between lowerBound and upperBound (inclusive)</returns>
|
||||
public int GetIndexCount(int lowerBound, int upperBound)
|
||||
{
|
||||
if (upperBound < lowerBound || _list.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int index = this.FindRangeIndex(lowerBound);
|
||||
if (IsCorrectRangeIndex(index, lowerBound))
|
||||
{
|
||||
count += _list[index].UpperBound - lowerBound + 1;
|
||||
}
|
||||
|
||||
index++;
|
||||
while (index < _list.Count && _list[index].UpperBound <= upperBound)
|
||||
{
|
||||
count += _list[index].Count;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index < _list.Count && IsCorrectRangeIndex(index, upperBound))
|
||||
{
|
||||
count += upperBound - _list[index].LowerBound;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of indexes in this table after a given startingIndex but before
|
||||
/// reaching a gap of indexes of a given size
|
||||
/// </summary>
|
||||
/// <param name="startingIndex">Index to start at</param>
|
||||
/// <param name="gapSize">Size of index gap</param>
|
||||
/// <returns>the number of indexes in this table after a given startingIndex but before
|
||||
/// reaching a gap of indexes of a given size</returns>
|
||||
public int GetIndexCountBeforeGap(int startingIndex, int gapSize)
|
||||
{
|
||||
if (_list.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int currentIndex = startingIndex;
|
||||
int rangeIndex = 0;
|
||||
int gap = 0;
|
||||
while (gap <= gapSize && rangeIndex < _list.Count)
|
||||
{
|
||||
gap += _list[rangeIndex].LowerBound - currentIndex;
|
||||
if (gap <= gapSize)
|
||||
{
|
||||
count += _list[rangeIndex].UpperBound - _list[rangeIndex].LowerBound + 1;
|
||||
currentIndex = _list[rangeIndex].UpperBound + 1;
|
||||
rangeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that goes through the indexes present in the table
|
||||
/// </summary>
|
||||
/// <returns>an enumerator that enumerates the indexes present in the table</returns>
|
||||
public IEnumerable<int> GetIndexes()
|
||||
{
|
||||
Debug.Assert(_list != null, "Expected non-null _list.");
|
||||
|
||||
foreach (Range<T> range in _list)
|
||||
{
|
||||
for (int i = range.LowerBound; i <= range.UpperBound; i++)
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all the indexes on or after a starting index
|
||||
/// </summary>
|
||||
/// <param name="startIndex">start index</param>
|
||||
/// <returns>all the indexes on or after a starting index</returns>
|
||||
public IEnumerable<int> GetIndexes(int startIndex)
|
||||
{
|
||||
Debug.Assert(_list != null, "Expected non-null _list.");
|
||||
|
||||
int rangeIndex = FindRangeIndex(startIndex);
|
||||
if (rangeIndex == -1)
|
||||
{
|
||||
rangeIndex++;
|
||||
}
|
||||
|
||||
while (rangeIndex < _list.Count)
|
||||
{
|
||||
for (int i = _list[rangeIndex].LowerBound; i <= _list[rangeIndex].UpperBound; i++)
|
||||
{
|
||||
if (i >= startIndex)
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
|
||||
rangeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the index of the Nth element in the table
|
||||
/// </summary>
|
||||
/// <param name="n">n</param>
|
||||
/// <returns>the index of the Nth element in the table</returns>
|
||||
public int GetNthIndex(int n)
|
||||
{
|
||||
Debug.Assert(n >= 0 && n < this.IndexCount, "Expected n between 0 and IndexCount-1, inclusive.");
|
||||
|
||||
int cumulatedEntries = 0;
|
||||
foreach (Range<T> range in _list)
|
||||
{
|
||||
if (cumulatedEntries + range.Count > n)
|
||||
{
|
||||
return range.LowerBound + n - cumulatedEntries;
|
||||
}
|
||||
else
|
||||
{
|
||||
cumulatedEntries += range.Count;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value at a given index or the default value if the index is not in the table
|
||||
/// </summary>
|
||||
/// <param name="index">index to search for</param>
|
||||
/// <returns>the value at the given index or the default value if index is not in the table</returns>
|
||||
public T GetValueAt(int index) => GetValueAt(index, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value at a given index or the default value if the index is not in the table
|
||||
/// </summary>
|
||||
/// <param name="index">index to search for</param>
|
||||
/// <param name="found">set to true by the method if the index was found; otherwise, false</param>
|
||||
/// <returns>the value at the given index or the default value if index is not in the table</returns>
|
||||
public T GetValueAt(int index, out bool found)
|
||||
{
|
||||
int rangeIndex = this.FindRangeIndex(index);
|
||||
if (this.IsCorrectRangeIndex(rangeIndex, index))
|
||||
{
|
||||
found = true;
|
||||
return _list[rangeIndex].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
found = false;
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an index's index within this table
|
||||
/// </summary>
|
||||
/// <param name="index">index to search for</param>
|
||||
/// <returns>an index's index within this table</returns>
|
||||
public int IndexOf(int index)
|
||||
{
|
||||
int cumulatedIndexes = 0;
|
||||
foreach (Range<T> range in _list)
|
||||
{
|
||||
if (range.UpperBound >= index)
|
||||
{
|
||||
cumulatedIndexes += index - range.LowerBound;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
cumulatedIndexes += range.Count;
|
||||
}
|
||||
}
|
||||
|
||||
return cumulatedIndexes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an index at the given location. This does not alter values in the table
|
||||
/// </summary>
|
||||
/// <param name="index">index location to insert an index</param>
|
||||
public void InsertIndex(int index)
|
||||
{
|
||||
InsertIndexes(index, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an index into the table with the given value
|
||||
/// </summary>
|
||||
/// <param name="index">index to insert</param>
|
||||
/// <param name="value">value for the index</param>
|
||||
public void InsertIndexAndValue(int index, T value)
|
||||
{
|
||||
InsertIndexesAndValues(index, 1, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts multiple indexes into the table. This does not alter Values in the table
|
||||
/// </summary>
|
||||
/// <param name="startIndex">first index to insert</param>
|
||||
/// <param name="count">total number of indexes to insert</param>
|
||||
public void InsertIndexes(int startIndex, int count)
|
||||
{
|
||||
Debug.Assert(count > 0, "Expected a strictly positive count parameter.");
|
||||
|
||||
InsertIndexesPrivate(startIndex, count, this.FindRangeIndex(startIndex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts multiple indexes into the table with the given value
|
||||
/// </summary>
|
||||
/// <param name="startIndex">Index to insert first value</param>
|
||||
/// <param name="count">Total number of values to insert (must be greater than 0)</param>
|
||||
/// <param name="value">Value to insert</param>
|
||||
public void InsertIndexesAndValues(int startIndex, int count, T value)
|
||||
{
|
||||
Debug.Assert(count > 0, "Expected a strictly positive count parameter.");
|
||||
|
||||
int lowerRangeIndex = this.FindRangeIndex(startIndex);
|
||||
InsertIndexesPrivate(startIndex, count, lowerRangeIndex);
|
||||
if ((lowerRangeIndex >= 0) && (_list[lowerRangeIndex].LowerBound > startIndex))
|
||||
{
|
||||
// Because of the insert, the original range no longer contains the startIndex
|
||||
lowerRangeIndex--;
|
||||
}
|
||||
|
||||
AddValuesPrivate(startIndex, count, value, lowerRangeIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an index from the table. This does not alter Values in the table
|
||||
/// </summary>
|
||||
/// <param name="index">index to remove</param>
|
||||
public void RemoveIndex(int index)
|
||||
{
|
||||
RemoveIndexes(index, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a value and its index from the table
|
||||
/// </summary>
|
||||
/// <param name="index">index to remove</param>
|
||||
public void RemoveIndexAndValue(int index)
|
||||
{
|
||||
RemoveIndexesAndValues(index, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes multiple indexes from the table. This does not alter Values in the table
|
||||
/// </summary>
|
||||
/// <param name="startIndex">first index to remove</param>
|
||||
/// <param name="count">total number of indexes to remove</param>
|
||||
public void RemoveIndexes(int startIndex, int count)
|
||||
{
|
||||
int lowerRangeIndex = this.FindRangeIndex(startIndex);
|
||||
if (lowerRangeIndex < 0)
|
||||
{
|
||||
lowerRangeIndex = 0;
|
||||
}
|
||||
|
||||
int i = lowerRangeIndex;
|
||||
while (i < _list.Count)
|
||||
{
|
||||
Range<T> range = _list[i];
|
||||
if (range.UpperBound >= startIndex)
|
||||
{
|
||||
if (range.LowerBound >= startIndex + count)
|
||||
{
|
||||
// Both bounds will remain after the removal
|
||||
range.LowerBound -= count;
|
||||
range.UpperBound -= count;
|
||||
}
|
||||
else
|
||||
{
|
||||
int currentIndex = i;
|
||||
if (range.LowerBound <= startIndex)
|
||||
{
|
||||
// Range gets split up
|
||||
if (range.UpperBound >= startIndex + count)
|
||||
{
|
||||
i++;
|
||||
_list.Insert(i, new Range<T>(startIndex, range.UpperBound - count, range.Value));
|
||||
}
|
||||
|
||||
range.UpperBound = startIndex - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
range.LowerBound = startIndex;
|
||||
range.UpperBound -= count;
|
||||
}
|
||||
|
||||
if (RemoveRangeIfInvalid(range, currentIndex))
|
||||
{
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!this.Merge(lowerRangeIndex))
|
||||
{
|
||||
this.Merge(lowerRangeIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes multiple values and their indexes from the table
|
||||
/// </summary>
|
||||
/// <param name="startIndex">first index to remove</param>
|
||||
/// <param name="count">total number of indexes to remove</param>
|
||||
public void RemoveIndexesAndValues(int startIndex, int count)
|
||||
{
|
||||
RemoveValues(startIndex, count);
|
||||
RemoveIndexes(startIndex, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a value from the table at the given index. This does not alter other indexes in the table
|
||||
/// </summary>
|
||||
/// <param name="index">index where value should be removed</param>
|
||||
public void RemoveValue(int index)
|
||||
{
|
||||
RemoveValues(index, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes multiple values from the table. This does not alter other indexes in the table
|
||||
/// </summary>
|
||||
/// <param name="startIndex">first index where values should be removed </param>
|
||||
/// <param name="count">total number of values to remove</param>
|
||||
public void RemoveValues(int startIndex, int count)
|
||||
{
|
||||
Debug.Assert(count > 0, "Expected a strictly positive count parameter.");
|
||||
|
||||
int lowerRangeIndex = this.FindRangeIndex(startIndex);
|
||||
if (lowerRangeIndex < 0)
|
||||
{
|
||||
lowerRangeIndex = 0;
|
||||
}
|
||||
|
||||
while ((lowerRangeIndex < _list.Count) && (_list[lowerRangeIndex].UpperBound < startIndex))
|
||||
{
|
||||
lowerRangeIndex++;
|
||||
}
|
||||
|
||||
if (lowerRangeIndex >= _list.Count || _list[lowerRangeIndex].LowerBound > startIndex + count - 1)
|
||||
{
|
||||
// If all the values are above our below our values, we have nothing to remove
|
||||
return;
|
||||
}
|
||||
|
||||
if (_list[lowerRangeIndex].LowerBound < startIndex)
|
||||
{
|
||||
// Need to split this up
|
||||
_list.Insert(lowerRangeIndex, new Range<T>(_list[lowerRangeIndex].LowerBound, startIndex - 1, _list[lowerRangeIndex].Value));
|
||||
lowerRangeIndex++;
|
||||
}
|
||||
|
||||
_list[lowerRangeIndex].LowerBound = startIndex + count;
|
||||
if (!RemoveRangeIfInvalid(_list[lowerRangeIndex], lowerRangeIndex))
|
||||
{
|
||||
lowerRangeIndex++;
|
||||
}
|
||||
|
||||
while ((lowerRangeIndex < _list.Count) && (_list[lowerRangeIndex].UpperBound < startIndex + count))
|
||||
{
|
||||
_list.RemoveAt(lowerRangeIndex);
|
||||
}
|
||||
|
||||
if ((lowerRangeIndex < _list.Count) && (_list[lowerRangeIndex].UpperBound >= startIndex + count) &&
|
||||
(_list[lowerRangeIndex].LowerBound < startIndex + count))
|
||||
{
|
||||
// Chop off the start of the remaining Range if it contains values that we're removing
|
||||
_list[lowerRangeIndex].LowerBound = startIndex + count;
|
||||
RemoveRangeIfInvalid(_list[lowerRangeIndex], lowerRangeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// End Public Methods
|
||||
|
||||
// Begin Private Methods
|
||||
private void AddValuesPrivate(int startIndex, int count, T value, int? startRangeIndex)
|
||||
{
|
||||
Debug.Assert(count > 0, "Expected a strictly positive count parameter.");
|
||||
|
||||
int endIndex = startIndex + count - 1;
|
||||
Range<T> newRange = new Range<T>(startIndex, endIndex, value);
|
||||
if (_list.Count == 0)
|
||||
{
|
||||
_list.Add(newRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
int lowerRangeIndex = startRangeIndex ?? this.FindRangeIndex(startIndex);
|
||||
Range<T> lowerRange = (lowerRangeIndex < 0) ? null : _list[lowerRangeIndex];
|
||||
if (lowerRange == null)
|
||||
{
|
||||
if (lowerRangeIndex < 0)
|
||||
{
|
||||
lowerRangeIndex = 0;
|
||||
}
|
||||
|
||||
_list.Insert(lowerRangeIndex, newRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!lowerRange.Value.Equals(value) && (lowerRange.UpperBound >= startIndex))
|
||||
{
|
||||
// Split up the range
|
||||
if (lowerRange.UpperBound > endIndex)
|
||||
{
|
||||
_list.Insert(lowerRangeIndex + 1, new Range<T>(endIndex + 1, lowerRange.UpperBound, lowerRange.Value));
|
||||
}
|
||||
|
||||
lowerRange.UpperBound = startIndex - 1;
|
||||
if (!RemoveRangeIfInvalid(lowerRange, lowerRangeIndex))
|
||||
{
|
||||
lowerRangeIndex++;
|
||||
}
|
||||
|
||||
_list.Insert(lowerRangeIndex, newRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
_list.Insert(lowerRangeIndex + 1, newRange);
|
||||
if (!Merge(lowerRangeIndex))
|
||||
{
|
||||
lowerRangeIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point the newRange has been inserted in the correct place, now we need to remove
|
||||
// any subsequent ranges that no longer make sense and possibly update the one at newRange.UpperBound
|
||||
int upperRangeIndex = lowerRangeIndex + 1;
|
||||
while ((upperRangeIndex < _list.Count) && (_list[upperRangeIndex].UpperBound < endIndex))
|
||||
{
|
||||
_list.RemoveAt(upperRangeIndex);
|
||||
}
|
||||
|
||||
if (upperRangeIndex < _list.Count)
|
||||
{
|
||||
Range<T> upperRange = _list[upperRangeIndex];
|
||||
if (upperRange.LowerBound <= endIndex)
|
||||
{
|
||||
// Update the range
|
||||
upperRange.LowerBound = endIndex + 1;
|
||||
RemoveRangeIfInvalid(upperRange, upperRangeIndex);
|
||||
}
|
||||
|
||||
Merge(lowerRangeIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the index of the range that contains the input or the range before if the input is not found
|
||||
private int FindRangeIndex(int index)
|
||||
{
|
||||
if (_list.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Do a binary search for the index
|
||||
int front = 0;
|
||||
int end = _list.Count - 1;
|
||||
Range<T> range;
|
||||
while (end > front)
|
||||
{
|
||||
int median = (front + end) / 2;
|
||||
range = _list[median];
|
||||
if (range.UpperBound < index)
|
||||
{
|
||||
front = median + 1;
|
||||
}
|
||||
else if (range.LowerBound > index)
|
||||
{
|
||||
end = median - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we found it
|
||||
return median;
|
||||
}
|
||||
}
|
||||
|
||||
if (front == end)
|
||||
{
|
||||
range = _list[front];
|
||||
if (range.ContainsIndex(index) || (range.UpperBound < index))
|
||||
{
|
||||
// we found it or the index isn't there and we're one range before
|
||||
return front;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not found and we're one range after
|
||||
return front - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// end is one index before front in this case so it's the range before
|
||||
return end;
|
||||
}
|
||||
}
|
||||
|
||||
private bool Merge(int lowerRangeIndex)
|
||||
{
|
||||
int upperRangeIndex = lowerRangeIndex + 1;
|
||||
if ((lowerRangeIndex >= 0) && (upperRangeIndex < _list.Count))
|
||||
{
|
||||
Range<T> lowerRange = _list[lowerRangeIndex];
|
||||
Range<T> upperRange = _list[upperRangeIndex];
|
||||
if (lowerRange.UpperBound + 1 >= upperRange.LowerBound && lowerRange.Value.Equals(upperRange.Value))
|
||||
{
|
||||
lowerRange.UpperBound = Math.Max(lowerRange.UpperBound, upperRange.UpperBound);
|
||||
_list.RemoveAt(upperRangeIndex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void InsertIndexesPrivate(int startIndex, int count, int lowerRangeIndex)
|
||||
{
|
||||
Debug.Assert(count > 0, "Expected a strictly positive count parameter.");
|
||||
|
||||
// Same as AddRange after we fix the indices affected by the insertion
|
||||
int startRangeIndex = (lowerRangeIndex >= 0) ? lowerRangeIndex : 0;
|
||||
for (int i = startRangeIndex; i < _list.Count; i++)
|
||||
{
|
||||
Range<T> range = _list[i];
|
||||
if (range.LowerBound >= startIndex)
|
||||
{
|
||||
range.LowerBound += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (range.UpperBound >= startIndex)
|
||||
{
|
||||
// Split up this range
|
||||
i++;
|
||||
_list.Insert(i, new Range<T>(startIndex, range.UpperBound + count, range.Value));
|
||||
range.UpperBound = startIndex - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (range.UpperBound >= startIndex)
|
||||
{
|
||||
range.UpperBound += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsCorrectRangeIndex(int rangeIndex, int index)
|
||||
{
|
||||
return rangeIndex != -1 && _list[rangeIndex].ContainsIndex(index);
|
||||
}
|
||||
|
||||
private bool RemoveRangeIfInvalid(Range<T> range, int rangeIndex)
|
||||
{
|
||||
if (range.UpperBound < range.LowerBound)
|
||||
{
|
||||
_list.RemoveAt(rangeIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// End Private Methods
|
||||
|
||||
// Begin IEnumerable<Range<T>> Members
|
||||
public IEnumerator<Range<T>> GetEnumerator()
|
||||
{
|
||||
return _list.GetEnumerator();
|
||||
}
|
||||
|
||||
// End IEnumerable<Range<T>> Members
|
||||
|
||||
// Begin IEnumerable Members
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _list.GetEnumerator();
|
||||
}
|
||||
|
||||
// End IEnumerable Members
|
||||
#if DEBUG
|
||||
|
||||
public void PrintIndexes()
|
||||
{
|
||||
Debug.WriteLine(this.IndexCount + " indexes");
|
||||
foreach (Range<T> range in _list)
|
||||
{
|
||||
Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} - {1}", range.LowerBound, range.UpperBound));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// 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.System;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Utilities
|
||||
{
|
||||
internal static class KeyboardHelper
|
||||
{
|
||||
public static void GetMetaKeyState(out bool ctrl, out bool shift)
|
||||
{
|
||||
ctrl = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down);
|
||||
shift = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down);
|
||||
}
|
||||
|
||||
public static void GetMetaKeyState(out bool ctrl, out bool shift, out bool alt)
|
||||
{
|
||||
GetMetaKeyState(out ctrl, out shift);
|
||||
alt = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Menu).HasFlag(CoreVirtualKeyStates.Down);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// 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.Utilities
|
||||
{
|
||||
internal class Range<T>
|
||||
{
|
||||
public Range(int lowerBound, int upperBound, T value)
|
||||
{
|
||||
LowerBound = lowerBound;
|
||||
UpperBound = upperBound;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return UpperBound - LowerBound + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public int LowerBound
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int UpperBound
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public T Value
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool ContainsIndex(int index)
|
||||
{
|
||||
return LowerBound <= index && UpperBound >= index;
|
||||
}
|
||||
|
||||
public bool ContainsValue(object value)
|
||||
{
|
||||
return (this.Value == null) ? value == null : this.Value.Equals(value);
|
||||
}
|
||||
|
||||
public Range<T> Copy()
|
||||
{
|
||||
return new Range<T>(LowerBound, UpperBound, Value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,478 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Utilities
|
||||
{
|
||||
internal static class TypeHelper
|
||||
{
|
||||
internal const char LeftIndexerToken = '[';
|
||||
internal const char PropertyNameSeparator = '.';
|
||||
internal const char RightIndexerToken = ']';
|
||||
|
||||
private static bool isAPIsAvailableInitialized = false;
|
||||
private static bool isXamlRootAvailable = false;
|
||||
|
||||
// Methods
|
||||
private static Type FindGenericType(Type definition, Type type)
|
||||
{
|
||||
TypeInfo definitionTypeInfo = definition.GetTypeInfo();
|
||||
|
||||
while (type != null && type != typeof(object))
|
||||
{
|
||||
TypeInfo typeTypeInfo = type.GetTypeInfo();
|
||||
|
||||
if (typeTypeInfo.IsGenericType && type.GetGenericTypeDefinition() == definition)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
if (definitionTypeInfo.IsInterface)
|
||||
{
|
||||
foreach (Type type2 in typeTypeInfo.ImplementedInterfaces)
|
||||
{
|
||||
Type type3 = FindGenericType(definition, type2);
|
||||
if (type3 != null)
|
||||
{
|
||||
return type3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type = typeTypeInfo.BaseType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an int or string indexer in the specified collection of members, where int indexers take priority
|
||||
/// over string indexers. If found, this method will return the associated PropertyInfo and set the out index
|
||||
/// argument to its appropriate value. If not found, the return value will be null, as will the index.
|
||||
/// </summary>
|
||||
/// <param name="members">Collection of members to search through for an indexer.</param>
|
||||
/// <param name="stringIndex">String value of indexer argument.</param>
|
||||
/// <param name="index">Resultant index value.</param>
|
||||
/// <returns>Indexer PropertyInfo if found, null otherwise.</returns>
|
||||
private static PropertyInfo FindIndexerInMembers(MemberInfo[] members, string stringIndex, out object[] index)
|
||||
{
|
||||
index = null;
|
||||
ParameterInfo[] parameters;
|
||||
PropertyInfo stringIndexer = null;
|
||||
|
||||
foreach (PropertyInfo pi in members)
|
||||
{
|
||||
if (pi == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only a single parameter is supported and it must be a string or Int32 value.
|
||||
parameters = pi.GetIndexParameters();
|
||||
if (parameters.Length > 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parameters[0].ParameterType == typeof(int))
|
||||
{
|
||||
int intIndex = -1;
|
||||
if (int.TryParse(stringIndex.Trim(), NumberStyles.None, CultureInfo.InvariantCulture, out intIndex))
|
||||
{
|
||||
index = new object[] { intIndex };
|
||||
return pi;
|
||||
}
|
||||
}
|
||||
|
||||
// If string indexer is found save it, in case there is an int indexer.
|
||||
if (parameters[0].ParameterType == typeof(string))
|
||||
{
|
||||
index = new object[] { stringIndex };
|
||||
stringIndexer = pi;
|
||||
}
|
||||
}
|
||||
|
||||
return stringIndexer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default member name that is used for an indexer (e.g. "Item").
|
||||
/// </summary>
|
||||
/// <param name="type">Type to check.</param>
|
||||
/// <returns>Default member name.</returns>
|
||||
private static string GetDefaultMemberName(this Type type)
|
||||
{
|
||||
DefaultMemberAttribute defaultMemberAttribute = type.GetTypeInfo().GetCustomAttributes().OfType<DefaultMemberAttribute>().FirstOrDefault();
|
||||
return defaultMemberAttribute == null ? null : defaultMemberAttribute.MemberName;
|
||||
}
|
||||
|
||||
internal static string GetBindingPropertyName(this Binding binding)
|
||||
{
|
||||
return binding?.Path?.Path?.Split('.')?.LastOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the PropertyInfo for the specified property path within this Type, and returns
|
||||
/// the value of GetShortName on its DisplayAttribute, if one exists. GetShortName will return
|
||||
/// the value of Name if there is no ShortName specified.
|
||||
/// </summary>
|
||||
/// <param name="type">Type to search</param>
|
||||
/// <param name="propertyPath">property path</param>
|
||||
/// <returns>DisplayAttribute.ShortName if it exists, null otherwise</returns>
|
||||
internal static string GetDisplayName(this Type type, string propertyPath)
|
||||
{
|
||||
PropertyInfo propertyInfo = type.GetNestedProperty(propertyPath);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
DisplayAttribute displayAttribute = propertyInfo.GetCustomAttributes().OfType<DisplayAttribute>().FirstOrDefault();
|
||||
return displayAttribute == null ? null : displayAttribute.GetShortName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static Type GetEnumerableItemType(this Type enumerableType)
|
||||
{
|
||||
Type type = FindGenericType(typeof(IEnumerable<>), enumerableType);
|
||||
if (type != null)
|
||||
{
|
||||
return type.GetGenericArguments()[0];
|
||||
}
|
||||
|
||||
return enumerableType;
|
||||
}
|
||||
|
||||
internal static PropertyInfo GetNestedProperty(this Type parentType, string propertyPath)
|
||||
{
|
||||
if (parentType != null)
|
||||
{
|
||||
object item = null;
|
||||
return parentType.GetNestedProperty(propertyPath, ref item);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the leaf PropertyInfo for the specified property path, and returns its value
|
||||
/// if the item is non-null.
|
||||
/// </summary>
|
||||
/// <param name="parentType">Type to search.</param>
|
||||
/// <param name="propertyPath">Property path.</param>
|
||||
/// <param name="item">Parent item which will be set to the property value if non-null.</param>
|
||||
/// <returns>The PropertyInfo.</returns>
|
||||
internal static PropertyInfo GetNestedProperty(this Type parentType, string propertyPath, ref object item)
|
||||
{
|
||||
if (parentType == null || string.IsNullOrEmpty(propertyPath))
|
||||
{
|
||||
item = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
PropertyInfo propertyInfo = null;
|
||||
Type propertyType = parentType;
|
||||
List<string> propertyNames = SplitPropertyPath(propertyPath);
|
||||
for (int i = 0; i < propertyNames.Count; i++)
|
||||
{
|
||||
propertyInfo = propertyType.GetPropertyOrIndexer(propertyNames[i], out var index);
|
||||
if (propertyInfo == null)
|
||||
{
|
||||
item = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
item = propertyInfo.GetValue(item, index);
|
||||
}
|
||||
|
||||
propertyType = propertyInfo.PropertyType.GetNonNullableType();
|
||||
}
|
||||
|
||||
return propertyInfo;
|
||||
}
|
||||
|
||||
internal static Type GetNestedPropertyType(this Type parentType, string propertyPath)
|
||||
{
|
||||
if (parentType == null || string.IsNullOrEmpty(propertyPath))
|
||||
{
|
||||
return parentType;
|
||||
}
|
||||
|
||||
PropertyInfo propertyInfo = parentType.GetNestedProperty(propertyPath);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return propertyInfo.PropertyType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a given property path on a particular data item.
|
||||
/// </summary>
|
||||
/// <param name="item">Parent data item.</param>
|
||||
/// <param name="propertyPath">Property path.</param>
|
||||
/// <returns>Value.</returns>
|
||||
internal static object GetNestedPropertyValue(object item, string propertyPath)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
Type parentType = item.GetCustomOrCLRType();
|
||||
if (string.IsNullOrEmpty(propertyPath))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
else if (parentType != null)
|
||||
{
|
||||
object nestedValue = item;
|
||||
parentType.GetNestedProperty(propertyPath, ref nestedValue);
|
||||
return nestedValue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static Type GetNonNullableType(this Type type)
|
||||
{
|
||||
if (IsNullableType(type))
|
||||
{
|
||||
return type.GetGenericArguments()[0];
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the PropertyInfo for the specified property path. If the property path
|
||||
/// refers to an indexer (e.g. "[abc]"), then the index out parameter will be set to the value
|
||||
/// specified in the property path. This method only supports indexers with a single parameter
|
||||
/// that is either an int or a string. Int parameters take priority over string parameters.
|
||||
/// </summary>
|
||||
/// <param name="type">Type to search.</param>
|
||||
/// <param name="propertyPath">Property path.</param>
|
||||
/// <param name="index">Set to the index if return value is an indexer, otherwise null.</param>
|
||||
/// <returns>PropertyInfo for either a property or an indexer.</returns>
|
||||
internal static PropertyInfo GetPropertyOrIndexer(this Type type, string propertyPath, out object[] index)
|
||||
{
|
||||
index = null;
|
||||
if (string.IsNullOrEmpty(propertyPath) || propertyPath[0] != LeftIndexerToken)
|
||||
{
|
||||
// Return the default value of GetProperty if the first character is not an indexer token.
|
||||
return type.GetProperty(propertyPath);
|
||||
}
|
||||
|
||||
if (propertyPath.Length < 2 || propertyPath[propertyPath.Length - 1] != RightIndexerToken)
|
||||
{
|
||||
// Return null if the indexer does not meet the standard format (i.e. "[x]").
|
||||
return null;
|
||||
}
|
||||
|
||||
var stringIndex = propertyPath.Substring(1, propertyPath.Length - 2);
|
||||
var indexer = FindIndexerInMembers(type.GetDefaultMembers(), stringIndex, out index);
|
||||
if (indexer != null)
|
||||
{
|
||||
// We found the indexer, so return it.
|
||||
return indexer;
|
||||
}
|
||||
|
||||
if (typeof(System.Collections.IList).IsAssignableFrom(type))
|
||||
{
|
||||
// If the object is of type IList, try to use its default indexer.
|
||||
indexer = FindIndexerInMembers(typeof(System.Collections.IList).GetDefaultMembers(), stringIndex, out index);
|
||||
}
|
||||
|
||||
return indexer;
|
||||
}
|
||||
|
||||
internal static bool IsEnumerableType(this Type enumerableType)
|
||||
{
|
||||
return FindGenericType(typeof(IEnumerable<>), enumerableType) != null;
|
||||
}
|
||||
|
||||
internal static bool IsNullableType(this Type type)
|
||||
{
|
||||
return type != null && type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||
}
|
||||
|
||||
/* Unused for now
|
||||
internal static bool IsNullableEnum(this Type type)
|
||||
{
|
||||
return type.IsNullableType() &&
|
||||
type.GenericTypeArguments.Length == 1 &&
|
||||
type.GenericTypeArguments[0].GetTypeInfo().IsEnum;
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// If the specified property is an indexer, this method will prepend the object's
|
||||
/// default member name to it (e.g. "[foo]" returns "Item[foo]").
|
||||
/// </summary>
|
||||
/// <param name="item">Declaring data item.</param>
|
||||
/// <param name="property">Property name.</param>
|
||||
/// <returns>Property with default member name prepended, or property if unchanged.</returns>
|
||||
internal static string PrependDefaultMemberName(object item, string property)
|
||||
{
|
||||
if (item != null && !string.IsNullOrEmpty(property) && property[0] == TypeHelper.LeftIndexerToken)
|
||||
{
|
||||
// The leaf property name is an indexer, so add the default member name.
|
||||
Type declaringType = item.GetCustomOrCLRType();
|
||||
if (declaringType != null)
|
||||
{
|
||||
string defaultMemberName = declaringType.GetNonNullableType().GetDefaultMemberName();
|
||||
if (!string.IsNullOrEmpty(defaultMemberName))
|
||||
{
|
||||
return defaultMemberName + property;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the specified property is an indexer, this method will remove the object's
|
||||
/// default member name from it (e.g. "Item[foo]" returns "[foo]").
|
||||
/// </summary>
|
||||
/// <param name="property">Property name.</param>
|
||||
/// <returns>Property with default member name removed, or property if unchanged.</returns>
|
||||
internal static string RemoveDefaultMemberName(string property)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(property) && property[property.Length - 1] == TypeHelper.RightIndexerToken)
|
||||
{
|
||||
// The property is an indexer, so remove the default member name.
|
||||
int leftIndexerToken = property.IndexOf(TypeHelper.LeftIndexerToken);
|
||||
if (leftIndexerToken >= 0)
|
||||
{
|
||||
return property.Substring(leftIndexerToken);
|
||||
}
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a given property path on a particular item.
|
||||
/// </summary>
|
||||
/// <param name="item">Parent data item.</param>
|
||||
/// <param name="newValue">New child value</param>
|
||||
/// <param name="propertyPath">Property path</param>
|
||||
internal static void SetNestedPropertyValue(ref object item, object newValue, string propertyPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(propertyPath))
|
||||
{
|
||||
item = newValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var propertyPathParts = SplitPropertyPath(propertyPath);
|
||||
|
||||
if (propertyPathParts.Count == 1)
|
||||
{
|
||||
item?.GetType().GetProperty(propertyPath)?.SetValue(item, newValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
object temporaryItem = item;
|
||||
object nextToLastItem = null;
|
||||
|
||||
PropertyInfo propertyInfo = null;
|
||||
|
||||
for (var i = 0; i < propertyPathParts.Count; i++)
|
||||
{
|
||||
propertyInfo = temporaryItem?.GetType().GetProperty(propertyPathParts[i]);
|
||||
|
||||
if (i == propertyPathParts.Count - 2)
|
||||
{
|
||||
nextToLastItem = propertyInfo?.GetValue(temporaryItem);
|
||||
}
|
||||
|
||||
temporaryItem = propertyInfo?.GetValue(temporaryItem);
|
||||
}
|
||||
|
||||
propertyInfo?.SetValue(nextToLastItem, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of substrings where each one represents a single property within a nested
|
||||
/// property path which may include indexers. For example, the string "abc.d[efg][h].ijk"
|
||||
/// would return the substrings: "abc", "d", "[efg]", "[h]", and "ijk".
|
||||
/// </summary>
|
||||
/// <param name="propertyPath">Path to split.</param>
|
||||
/// <returns>List of property substrings.</returns>
|
||||
internal static List<string> SplitPropertyPath(string propertyPath)
|
||||
{
|
||||
List<string> propertyPaths = new List<string>();
|
||||
if (!string.IsNullOrEmpty(propertyPath))
|
||||
{
|
||||
int startIndex = 0;
|
||||
for (int index = 0; index < propertyPath.Length; index++)
|
||||
{
|
||||
if (propertyPath[index] == PropertyNameSeparator)
|
||||
{
|
||||
propertyPaths.Add(propertyPath.Substring(startIndex, index - startIndex));
|
||||
startIndex = index + 1;
|
||||
}
|
||||
else if (startIndex != index && propertyPath[index] == LeftIndexerToken)
|
||||
{
|
||||
propertyPaths.Add(propertyPath.Substring(startIndex, index - startIndex));
|
||||
startIndex = index;
|
||||
}
|
||||
else if (index == propertyPath.Length - 1)
|
||||
{
|
||||
propertyPaths.Add(propertyPath.Substring(startIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return propertyPaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns instance.GetCustomType() if the instance implements ICustomTypeProvider; otherwise,
|
||||
/// returns instance.GetType().
|
||||
/// </summary>
|
||||
/// <param name="instance">Object to return the type of</param>
|
||||
/// <returns>Type of the instance</returns>
|
||||
internal static Type GetCustomOrCLRType(this object instance)
|
||||
{
|
||||
ICustomTypeProvider customTypeProvider = instance as ICustomTypeProvider;
|
||||
if (customTypeProvider != null)
|
||||
{
|
||||
return customTypeProvider.GetCustomType() ?? instance.GetType();
|
||||
}
|
||||
|
||||
return instance == null ? null : instance.GetType();
|
||||
}
|
||||
|
||||
internal static bool IsXamlRootAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!isAPIsAvailableInitialized)
|
||||
{
|
||||
InitializeAPIsAvailable();
|
||||
}
|
||||
|
||||
return isXamlRootAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InitializeAPIsAvailable()
|
||||
{
|
||||
isXamlRootAvailable = Windows.Foundation.Metadata.ApiInformation.IsPropertyPresent("Windows.UI.Xaml.UIElement", "XamlRoot");
|
||||
isAPIsAvailableInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// 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.ViewManagement;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for accessing UISettings properties.
|
||||
/// </summary>
|
||||
internal static class UISettingsHelper
|
||||
{
|
||||
private static UISettings _uiSettings = null;
|
||||
|
||||
internal static bool AreSettingsEnablingAnimations
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_uiSettings == null)
|
||||
{
|
||||
_uiSettings = new UISettings();
|
||||
}
|
||||
|
||||
return _uiSettings.AnimationsEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool AreSettingsAutoHidingScrollBars
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO: Use UISettings public API once available
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Data.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class with methods to help with validation.
|
||||
/// </summary>
|
||||
internal static class ValidationUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a new ValidationResult to the collection if an equivalent does not exist.
|
||||
/// </summary>
|
||||
/// <param name="collection">ValidationResults to search through</param>
|
||||
/// <param name="value">ValidationResult to add</param>
|
||||
public static void AddIfNew(this ICollection<ValidationResult> collection, ValidationResult value)
|
||||
{
|
||||
if (!collection.ContainsEqualValidationResult(value))
|
||||
{
|
||||
collection.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs an action and catches any non-critical exceptions.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to perform</param>
|
||||
public static void CatchNonCriticalExceptions(Action action)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (IsCriticalException(exception))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
// Catch any non-critical exceptions
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the collection contains an equivalent ValidationResult
|
||||
/// </summary>
|
||||
/// <param name="collection">ValidationResults to search through</param>
|
||||
/// <param name="target">ValidationResult to search for</param>
|
||||
/// <returns>True when the collection contains an equivalent ValidationResult.</returns>
|
||||
public static bool ContainsEqualValidationResult(this ICollection<ValidationResult> collection, ValidationResult target)
|
||||
{
|
||||
return collection.FindEqualValidationResult(target) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches a ValidationResult for the specified target member name. If the target is null
|
||||
/// or empty, this method will return true if there are no member names at all.
|
||||
/// </summary>
|
||||
/// <param name="validationResult">ValidationResult to search.</param>
|
||||
/// <param name="target">Member name to search for.</param>
|
||||
/// <returns>True if found.</returns>
|
||||
public static bool ContainsMemberName(this ValidationResult validationResult, string target)
|
||||
{
|
||||
int memberNameCount = 0;
|
||||
foreach (string memberName in validationResult.MemberNames)
|
||||
{
|
||||
if (string.Equals(target, memberName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
memberNameCount++;
|
||||
}
|
||||
|
||||
return memberNameCount == 0 && string.IsNullOrEmpty(target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an equivalent ValidationResult if one exists.
|
||||
/// </summary>
|
||||
/// <param name="collection">ValidationResults to search through.</param>
|
||||
/// <param name="target">ValidationResult to find.</param>
|
||||
/// <returns>Equal ValidationResult if found, null otherwise.</returns>
|
||||
public static ValidationResult FindEqualValidationResult(this ICollection<ValidationResult> collection, ValidationResult target)
|
||||
{
|
||||
foreach (ValidationResult oldValidationResult in collection)
|
||||
{
|
||||
if (oldValidationResult.ErrorMessage == target.ErrorMessage)
|
||||
{
|
||||
bool movedOld = true;
|
||||
bool movedTarget = true;
|
||||
IEnumerator<string> oldEnumerator = oldValidationResult.MemberNames.GetEnumerator();
|
||||
IEnumerator<string> targetEnumerator = target.MemberNames.GetEnumerator();
|
||||
while (movedOld && movedTarget)
|
||||
{
|
||||
movedOld = oldEnumerator.MoveNext();
|
||||
movedTarget = targetEnumerator.MoveNext();
|
||||
|
||||
if (!movedOld && !movedTarget)
|
||||
{
|
||||
return oldValidationResult;
|
||||
}
|
||||
|
||||
if (movedOld != movedTarget || oldEnumerator.Current != targetEnumerator.Current)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches through all Bindings on the specified element and returns a list of BindingInfo objects
|
||||
/// for each Binding that matches the specified criteria.
|
||||
/// </summary>
|
||||
/// <param name="element">FrameworkElement to search</param>
|
||||
/// <param name="dataItem">Only return Bindings with a context element equal to this object</param>
|
||||
/// <param name="twoWayOnly">If true, only returns TwoWay Bindings</param>
|
||||
/// <param name="useBlockList">If true, ignores elements not typically used for input</param>
|
||||
/// <param name="searchChildren">If true, searches through the children</param>
|
||||
/// <param name="excludedTypes">The Binding search will skip all of these Types</param>
|
||||
/// <returns>List of BindingInfo for every Binding found</returns>
|
||||
public static List<BindingInfo> GetBindingInfo(this FrameworkElement element, object dataItem, bool twoWayOnly, bool useBlockList, bool searchChildren, params Type[] excludedTypes)
|
||||
{
|
||||
List<BindingInfo> bindingData = new List<BindingInfo>();
|
||||
|
||||
if (!searchChildren)
|
||||
{
|
||||
if (excludedTypes != null)
|
||||
{
|
||||
foreach (Type excludedType in excludedTypes)
|
||||
{
|
||||
if (excludedType != null && excludedType.IsInstanceOfType(element))
|
||||
{
|
||||
return bindingData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return element.GetBindingInfoOfSingleElement(element.DataContext ?? dataItem, dataItem, twoWayOnly, useBlockList);
|
||||
}
|
||||
|
||||
Stack<DependencyObject> children = new Stack<DependencyObject>();
|
||||
Stack<object> dataContexts = new Stack<object>();
|
||||
children.Push(element);
|
||||
dataContexts.Push(element.DataContext ?? dataItem);
|
||||
|
||||
while (children.Count != 0)
|
||||
{
|
||||
bool searchChild = true;
|
||||
DependencyObject child = children.Pop();
|
||||
object inheritedDataContext = dataContexts.Pop();
|
||||
object dataContext = inheritedDataContext;
|
||||
|
||||
// Skip this particular child element if it is one of the excludedTypes
|
||||
if (excludedTypes != null)
|
||||
{
|
||||
foreach (Type excludedType in excludedTypes)
|
||||
{
|
||||
if (excludedType != null && excludedType.IsInstanceOfType(child))
|
||||
{
|
||||
searchChild = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the bindings of the child element and push its children onto the stack of remaining elements to search
|
||||
if (searchChild)
|
||||
{
|
||||
FrameworkElement childElement = child as FrameworkElement;
|
||||
if (childElement != null)
|
||||
{
|
||||
dataContext = childElement.DataContext ?? inheritedDataContext;
|
||||
bindingData.AddRange(childElement.GetBindingInfoOfSingleElement(inheritedDataContext, dataItem, twoWayOnly, useBlockList));
|
||||
}
|
||||
|
||||
int childrenCount = VisualTreeHelper.GetChildrenCount(child);
|
||||
for (int childIndex = 0; childIndex < childrenCount; childIndex++)
|
||||
{
|
||||
children.Push(VisualTreeHelper.GetChild(child, childIndex));
|
||||
dataContexts.Push(dataContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bindingData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of the specified FrameworkElement's DependencyProperties. This method will return all
|
||||
/// DependencyProperties of the element unless 'useBlockList' is true, in which case all bindings on elements
|
||||
/// that are typically not used as input controls will be ignored.
|
||||
/// </summary>
|
||||
/// <param name="element">FrameworkElement of interest</param>
|
||||
/// <param name="useBlockList">If true, ignores elements not typically used for input</param>
|
||||
/// <returns>List of DependencyProperties</returns>
|
||||
public static List<DependencyProperty> GetDependencyProperties(this FrameworkElement element, bool useBlockList)
|
||||
{
|
||||
List<DependencyProperty> dependencyProperties = new List<DependencyProperty>();
|
||||
|
||||
bool isBlocklisted = useBlockList &&
|
||||
(element is Panel || element is Button || element is Image || element is ScrollViewer || element is TextBlock ||
|
||||
element is Border || element is Shape || element is ContentPresenter);
|
||||
|
||||
if (!isBlocklisted)
|
||||
{
|
||||
Type type = element.GetType();
|
||||
FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
|
||||
foreach (FieldInfo field in fields)
|
||||
{
|
||||
if (field.FieldType == typeof(DependencyProperty))
|
||||
{
|
||||
dependencyProperties.Add((DependencyProperty)field.GetValue(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dependencyProperties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified exception is un-recoverable.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <returns>True if the process cannot be recovered from the exception.</returns>
|
||||
public static bool IsCriticalException(Exception exception)
|
||||
{
|
||||
return (exception is OutOfMemoryException) ||
|
||||
(exception is StackOverflowException) ||
|
||||
(exception is AccessViolationException) ||
|
||||
(exception is ThreadAbortException);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of active bindings on the specified FrameworkElement. Bindings are gathered
|
||||
/// according to the same conditions BindingGroup uses to find bindings of descendant elements
|
||||
/// within the visual tree.
|
||||
/// </summary>
|
||||
/// <param name="element">Root FrameworkElement to search under</param>
|
||||
/// <param name="inheritedDataContext">DomainContext of the element's parent</param>
|
||||
/// <param name="dataItem">Target DomainContext</param>
|
||||
/// <param name="twoWayOnly">If true, only returns TwoWay Bindings</param>
|
||||
/// <param name="useBlockList">If true, ignores elements not typically used for input</param>
|
||||
/// <returns>List of active bindings on the specified FrameworkElement.</returns>
|
||||
private static List<BindingInfo> GetBindingInfoOfSingleElement(this FrameworkElement element, object inheritedDataContext, object dataItem, bool twoWayOnly, bool useBlockList)
|
||||
{
|
||||
// Now see which of the possible dependency properties are being used
|
||||
List<BindingInfo> bindingData = new List<BindingInfo>();
|
||||
foreach (DependencyProperty bindingTarget in element.GetDependencyProperties(useBlockList))
|
||||
{
|
||||
// We add bindings according to the same conditions as BindingGroups:
|
||||
// Element.Binding.Mode == TwoWay
|
||||
// Element.Binding.Source == null
|
||||
// DataItem == ContextElement.DataContext where:
|
||||
// If Element is ContentPresenter and TargetProperty is Content, ContextElement = Element.Parent
|
||||
// Else if TargetProperty is DomainContext, ContextElement = Element.Parent
|
||||
// Else ContextElement = Element
|
||||
BindingExpression bindingExpression = element.GetBindingExpression(bindingTarget);
|
||||
if (bindingExpression != null &&
|
||||
bindingExpression.ParentBinding != null &&
|
||||
(!twoWayOnly || bindingExpression.ParentBinding.Mode == BindingMode.TwoWay) &&
|
||||
bindingExpression.ParentBinding.Source == null)
|
||||
{
|
||||
object dataContext;
|
||||
if (bindingTarget == FrameworkElement.DataContextProperty
|
||||
|| (element is ContentPresenter && bindingTarget == ContentPresenter.ContentProperty))
|
||||
{
|
||||
dataContext = inheritedDataContext;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataContext = element.DataContext ?? inheritedDataContext;
|
||||
}
|
||||
|
||||
if (dataItem == dataContext)
|
||||
{
|
||||
bindingData.Add(new BindingInfo(bindingExpression, bindingTarget, element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bindingData;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
// 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.Diagnostics;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Names and helpers for visual states in the control.
|
||||
/// </summary>
|
||||
internal static class VisualStates
|
||||
{
|
||||
// GroupCommon
|
||||
|
||||
/// <summary>
|
||||
/// Normal state
|
||||
/// </summary>
|
||||
public const string StateNormal = "Normal";
|
||||
|
||||
/// <summary>
|
||||
/// PointerOver state
|
||||
/// </summary>
|
||||
public const string StatePointerOver = "PointerOver";
|
||||
|
||||
/// <summary>
|
||||
/// Pressed state
|
||||
/// </summary>
|
||||
public const string StatePressed = "Pressed";
|
||||
|
||||
/// <summary>
|
||||
/// Disabled state
|
||||
/// </summary>
|
||||
public const string StateDisabled = "Disabled";
|
||||
|
||||
/// <summary>
|
||||
/// Common state group
|
||||
/// </summary>
|
||||
public const string GroupCommon = "CommonStates";
|
||||
|
||||
// GroupExpanded
|
||||
|
||||
/// <summary>
|
||||
/// Expanded state
|
||||
/// </summary>
|
||||
public const string StateExpanded = "Expanded";
|
||||
|
||||
/// <summary>
|
||||
/// Collapsed state
|
||||
/// </summary>
|
||||
public const string StateCollapsed = "Collapsed";
|
||||
|
||||
/// <summary>
|
||||
/// Empty state
|
||||
/// </summary>
|
||||
public const string StateEmpty = "Empty";
|
||||
|
||||
// GroupFocus
|
||||
|
||||
/// <summary>
|
||||
/// Unfocused state
|
||||
/// </summary>
|
||||
public const string StateUnfocused = "Unfocused";
|
||||
|
||||
/// <summary>
|
||||
/// Focused state
|
||||
/// </summary>
|
||||
public const string StateFocused = "Focused";
|
||||
|
||||
/// <summary>
|
||||
/// Focus state group
|
||||
/// </summary>
|
||||
public const string GroupFocus = "FocusStates";
|
||||
|
||||
// GroupSelection
|
||||
|
||||
/// <summary>
|
||||
/// Selected state
|
||||
/// </summary>
|
||||
public const string StateSelected = "Selected";
|
||||
|
||||
/// <summary>
|
||||
/// Unselected state
|
||||
/// </summary>
|
||||
public const string StateUnselected = "Unselected";
|
||||
|
||||
/// <summary>
|
||||
/// Selection state group
|
||||
/// </summary>
|
||||
public const string GroupSelection = "SelectionStates";
|
||||
|
||||
// GroupActive
|
||||
|
||||
/// <summary>
|
||||
/// Active state
|
||||
/// </summary>
|
||||
public const string StateActive = "Active";
|
||||
|
||||
/// <summary>
|
||||
/// Inactive state
|
||||
/// </summary>
|
||||
public const string StateInactive = "Inactive";
|
||||
|
||||
/// <summary>
|
||||
/// Active state group
|
||||
/// </summary>
|
||||
public const string GroupActive = "ActiveStates";
|
||||
|
||||
// GroupCurrent
|
||||
|
||||
/// <summary>
|
||||
/// Regular state
|
||||
/// </summary>
|
||||
public const string StateRegular = "Regular";
|
||||
|
||||
/// <summary>
|
||||
/// Current state
|
||||
/// </summary>
|
||||
public const string StateCurrent = "Current";
|
||||
|
||||
/// <summary>
|
||||
/// CurrentWithFocus state
|
||||
/// </summary>
|
||||
public const string StateCurrentWithFocus = "CurrentWithFocus";
|
||||
|
||||
/// <summary>
|
||||
/// Current state group
|
||||
/// </summary>
|
||||
public const string GroupCurrent = "CurrentStates";
|
||||
|
||||
// GroupInteraction
|
||||
|
||||
/// <summary>
|
||||
/// Display state
|
||||
/// </summary>
|
||||
public const string StateDisplay = "Display";
|
||||
|
||||
/// <summary>
|
||||
/// Editing state
|
||||
/// </summary>
|
||||
public const string StateEditing = "Editing";
|
||||
|
||||
/// <summary>
|
||||
/// Interaction state group
|
||||
/// </summary>
|
||||
public const string GroupInteraction = "InteractionStates";
|
||||
|
||||
// GroupSort
|
||||
|
||||
/// <summary>
|
||||
/// Unsorted state
|
||||
/// </summary>
|
||||
public const string StateUnsorted = "Unsorted";
|
||||
|
||||
/// <summary>
|
||||
/// Sort Ascending state
|
||||
/// </summary>
|
||||
public const string StateSortAscending = "SortAscending";
|
||||
|
||||
/// <summary>
|
||||
/// Sort Descending state
|
||||
/// </summary>
|
||||
public const string StateSortDescending = "SortDescending";
|
||||
|
||||
/// <summary>
|
||||
/// Sort state group
|
||||
/// </summary>
|
||||
public const string GroupSort = "SortStates";
|
||||
|
||||
// GroupValidation
|
||||
|
||||
/// <summary>
|
||||
/// Invalid state
|
||||
/// </summary>
|
||||
public const string StateInvalid = "Invalid";
|
||||
|
||||
/// <summary>
|
||||
/// RowInvalid state
|
||||
/// </summary>
|
||||
public const string StateRowInvalid = "RowInvalid";
|
||||
|
||||
/// <summary>
|
||||
/// RowValid state
|
||||
/// </summary>
|
||||
public const string StateRowValid = "RowValid";
|
||||
|
||||
/// <summary>
|
||||
/// Valid state
|
||||
/// </summary>
|
||||
public const string StateValid = "Valid";
|
||||
|
||||
#if FEATURE_VALIDATION
|
||||
// RuntimeValidationStates
|
||||
public const string StateInvalidUnfocused = "InvalidUnfocused";
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Validation state group
|
||||
/// </summary>
|
||||
public const string GroupValidation = "ValidationStates";
|
||||
|
||||
// GroupScrollBarsSeparator
|
||||
|
||||
/// <summary>
|
||||
/// SeparatorExpanded state
|
||||
/// </summary>
|
||||
public const string StateSeparatorExpanded = "SeparatorExpanded";
|
||||
|
||||
/// <summary>
|
||||
/// ScrollBarsSeparatorCollapsed state
|
||||
/// </summary>
|
||||
public const string StateSeparatorCollapsed = "SeparatorCollapsed";
|
||||
|
||||
/// <summary>
|
||||
/// SeparatorExpandedWithoutAnimation state
|
||||
/// </summary>
|
||||
public const string StateSeparatorExpandedWithoutAnimation = "SeparatorExpandedWithoutAnimation";
|
||||
|
||||
/// <summary>
|
||||
/// SeparatorCollapsedWithoutAnimation state
|
||||
/// </summary>
|
||||
public const string StateSeparatorCollapsedWithoutAnimation = "SeparatorCollapsedWithoutAnimation";
|
||||
|
||||
/// <summary>
|
||||
/// ScrollBarsSeparator state group
|
||||
/// </summary>
|
||||
public const string GroupScrollBarsSeparator = "ScrollBarsSeparatorStates";
|
||||
|
||||
// GroupScrollBars
|
||||
|
||||
/// <summary>
|
||||
/// TouchIndicator state
|
||||
/// </summary>
|
||||
public const string StateTouchIndicator = "TouchIndicator";
|
||||
|
||||
/// <summary>
|
||||
/// MouseIndicator state
|
||||
/// </summary>
|
||||
public const string StateMouseIndicator = "MouseIndicator";
|
||||
|
||||
/// <summary>
|
||||
/// MouseIndicatorFull state
|
||||
/// </summary>
|
||||
public const string StateMouseIndicatorFull = "MouseIndicatorFull";
|
||||
|
||||
/// <summary>
|
||||
/// NoIndicator state
|
||||
/// </summary>
|
||||
public const string StateNoIndicator = "NoIndicator";
|
||||
|
||||
/// <summary>
|
||||
/// ScrollBars state group
|
||||
/// </summary>
|
||||
public const string GroupScrollBars = "ScrollBarsStates";
|
||||
|
||||
/// <summary>
|
||||
/// Use VisualStateManager to change the visual state of the control.
|
||||
/// </summary>
|
||||
/// <param name="control">
|
||||
/// Control whose visual state is being changed.
|
||||
/// </param>
|
||||
/// <param name="useTransitions">
|
||||
/// true to use transitions when updating the visual state, false to
|
||||
/// snap directly to the new visual state.
|
||||
/// </param>
|
||||
/// <param name="stateNames">
|
||||
/// Ordered list of state names and fallback states to transition into.
|
||||
/// Only the first state to be found will be used.
|
||||
/// </param>
|
||||
public static void GoToState(Control control, bool useTransitions, params string[] stateNames)
|
||||
{
|
||||
Debug.Assert(control != null, "Expected non-null control.");
|
||||
|
||||
if (stateNames == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string name in stateNames)
|
||||
{
|
||||
if (VisualStateManager.GoToState(control, name, useTransitions))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// 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.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a weak event listener that allows the owner to be garbage
|
||||
/// collected if its only remaining link is an event handler.
|
||||
/// Note: Copied from Microsoft.Toolkit.Uwp.Helpers.WeakEventListener to avoid taking a
|
||||
/// dependency on Microsoft.Toolkit.Uwp.dll and Microsoft.Toolkit.dll.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInstance">Type of instance listening for the event.</typeparam>
|
||||
/// <typeparam name="TSource">Type of source for the event.</typeparam>
|
||||
/// <typeparam name="TEventArgs">Type of event arguments for the event.</typeparam>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal sealed class WeakEventListener<TInstance, TSource, TEventArgs>
|
||||
where TInstance : class
|
||||
{
|
||||
/// <summary>
|
||||
/// WeakReference to the instance listening for the event.
|
||||
/// </summary>
|
||||
private WeakReference<TInstance> weakInstance;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeakEventListener{TInstance, TSource, TEventArgs}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance subscribing to the event.</param>
|
||||
public WeakEventListener(TInstance instance)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(instance));
|
||||
}
|
||||
|
||||
weakInstance = new WeakReference<TInstance>(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method to call when the event fires.
|
||||
/// </summary>
|
||||
public Action<TInstance, TSource, TEventArgs> OnEventAction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method to call when detaching from the event.
|
||||
/// </summary>
|
||||
public Action<WeakEventListener<TInstance, TSource, TEventArgs>> OnDetachAction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the subscribed event calls OnEventAction to handle it.
|
||||
/// </summary>
|
||||
/// <param name="source">Event source.</param>
|
||||
/// <param name="eventArgs">Event arguments.</param>
|
||||
public void OnEvent(TSource source, TEventArgs eventArgs)
|
||||
{
|
||||
if (weakInstance.TryGetTarget(out var target))
|
||||
{
|
||||
// Call registered action
|
||||
OnEventAction?.Invoke(target, source, eventArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Detach from event
|
||||
Detach();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches from the subscribed event.
|
||||
/// </summary>
|
||||
public void Detach()
|
||||
{
|
||||
OnDetachAction?.Invoke(this);
|
||||
OnDetachAction = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<FileList>
|
||||
<File Reference="Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.dll">
|
||||
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.DataGrid" />
|
||||
</ToolboxItems>
|
||||
</File>
|
||||
</FileList>
|
|
@ -0,0 +1,45 @@
|
|||
jobs:
|
||||
- job: Windows
|
||||
pool:
|
||||
vmImage: windows-2019
|
||||
steps:
|
||||
- task: gitversion/setup@0
|
||||
displayName: Install GitVersion
|
||||
inputs:
|
||||
versionSpec: '5.6.3'
|
||||
|
||||
- task: gitversion/execute@0
|
||||
displayName: GitVersion
|
||||
inputs:
|
||||
useConfigFile: true
|
||||
configFilePath: gitversion.yml
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Build Uno.WinUI3Convert
|
||||
inputs:
|
||||
command: build
|
||||
arguments: --verbosity detailed --configuration Release "-p:PackageOutputPath=$(Build.ArtifactStagingDirectory)" "-p:PackageVersion=$(GITVERSION.SemVer)"
|
||||
workingDirectory: src/Uno.WinUI3Convert
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Install Uno.WinUI3Convert
|
||||
inputs:
|
||||
command: custom
|
||||
custom: tool
|
||||
arguments: install --global --add-source $(Build.ArtifactStagingDirectory) --version $(GITVERSION.SemVer) uno.winui3convert
|
||||
|
||||
- powershell: winui3convert Tests\Microsoft.Toolkit.Uwp.UI.Controls.DataGrid Tests\out\Microsoft.Toolkit.Uwp.UI.Controls.DataGrid
|
||||
displayName: Convert Microsoft.Toolkit.Uwp.UI.Controls.DataGrid sources
|
||||
|
||||
- task: MSBuild@1
|
||||
displayName: Build Microsoft.Toolkit.Uwp.UI.Controls.DataGrid
|
||||
inputs:
|
||||
solution: Tests/out/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.csproj
|
||||
configuration: Release
|
||||
msbuildArguments: /ds /r
|
||||
maximumCpuCount: true
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifacts
|
||||
inputs:
|
||||
artifactName: Tool
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 8.0 KiB |
|
@ -0,0 +1,54 @@
|
|||
assembly-versioning-scheme: MajorMinorPatch
|
||||
mode: Mainline
|
||||
next-version: 1.0
|
||||
|
||||
branches:
|
||||
master:
|
||||
mode: ContinuousDeployment
|
||||
regex: master
|
||||
tag: dev
|
||||
increment: Minor
|
||||
is-source-branch-for: ['beta', 'stable']
|
||||
|
||||
pull-request:
|
||||
regex: ^(pull|pull\-requests|pr)[/-]
|
||||
mode: ContinuousDeployment
|
||||
tag: 'PullRequest'
|
||||
tag-number-pattern: '[/-](?<number>\d+)[-/]'
|
||||
increment: Inherit
|
||||
|
||||
beta:
|
||||
mode: ContinuousDeployment
|
||||
regex: ^release/beta/.*
|
||||
tag: beta
|
||||
increment: none
|
||||
source-branches: ['master']
|
||||
|
||||
stable:
|
||||
regex: ^release/stable/.*
|
||||
tag: ''
|
||||
increment: Patch
|
||||
source-branches: ['master','beta']
|
||||
is-mainline: true
|
||||
|
||||
dev:
|
||||
mode: ContinuousDeployment
|
||||
regex: ^dev/.*?/(.*?)
|
||||
tag: dev.{BranchName}
|
||||
source-branches: ['master', 'release', 'projects', 'feature']
|
||||
increment: none
|
||||
|
||||
projects:
|
||||
tag: proj-{BranchName}
|
||||
regex: ^projects/(.*?)
|
||||
source-branches: ['master']
|
||||
increment: none
|
||||
|
||||
feature:
|
||||
tag: feature.{BranchName}
|
||||
regex: ^feature/(.*?)
|
||||
source-branches: ['master']
|
||||
increment: none
|
||||
|
||||
ignore:
|
||||
sha: []
|
|
@ -0,0 +1,24 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<Authors>nventive</Authors>
|
||||
<Copyright>Copyright (C) 2020-$([System.DateTime]::Now.ToString(`yyyy`)) nventive inc. - all rights reserved</Copyright>
|
||||
<PackageIcon>uno-logo.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/unoplatform/winui3-convert</PackageProjectUrl>
|
||||
<RepositoryUrl>$(BUILD_REPOSITORY_URI)</RepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)..\build\uno-logo.png" Pack="true" Visible="false" PackagePath="\"/>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(SourceLinkEnabled)' != 'false'">
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(SourceLinkEnabled)' != 'false'">
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30711.63
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Uno.WinUI3Convert", "Uno.WinUI3Convert\Uno.WinUI3Convert.csproj", "{DB254870-0503-4C62-8985-6D50885A3542}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DB254870-0503-4C62-8985-6D50885A3542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DB254870-0503-4C62-8985-6D50885A3542}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DB254870-0503-4C62-8985-6D50885A3542}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DB254870-0503-4C62-8985-6D50885A3542}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {16F1C62D-11B1-457C-B75C-E6F2105014E1}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,180 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Uno.WinUI3Convert
|
||||
{
|
||||
public class ConvertCommand
|
||||
{
|
||||
public static int Execute(DirectoryInfo source, DirectoryInfo destination, bool overwrite, IHost host)
|
||||
{
|
||||
var logger =
|
||||
host.Services
|
||||
.GetRequiredService<ILoggerFactory>()
|
||||
.CreateLogger<ConvertCommand>();
|
||||
|
||||
try
|
||||
{
|
||||
if (destination.Exists)
|
||||
{
|
||||
if (overwrite)
|
||||
{
|
||||
destination.Delete(true);
|
||||
|
||||
logger.LogInformation($"Directory \"{destination}\" deleted.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogError("Destination exists and overwrite flag is not set.");
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
destination.Create();
|
||||
|
||||
logger.LogInformation($"Copying files to \"{destination}\"...");
|
||||
|
||||
CopyFiles(source, destination);
|
||||
|
||||
logger.LogInformation($"Rewriting files...");
|
||||
|
||||
RewriteFiles(destination, logger);
|
||||
|
||||
logger.LogInformation($"Rewriting projects...");
|
||||
|
||||
RewriteProjects(destination, logger);
|
||||
|
||||
logger.LogInformation($"Done.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "An error occurred.");
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyFiles(DirectoryInfo source, DirectoryInfo destination)
|
||||
{
|
||||
var subdirectories = source.EnumerateDirectories("*", SearchOption.AllDirectories);
|
||||
|
||||
foreach (var directory in subdirectories)
|
||||
{
|
||||
var pathFromBase = Path.GetRelativePath(source.FullName, directory.FullName);
|
||||
|
||||
Directory.CreateDirectory(Path.Combine(destination.FullName, pathFromBase));
|
||||
}
|
||||
|
||||
var files = source.EnumerateFiles("*", SearchOption.AllDirectories);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var pathFromBase = Path.GetRelativePath(source.FullName, file.FullName);
|
||||
|
||||
File.Copy(file.FullName, Path.Combine(destination.FullName, pathFromBase));
|
||||
}
|
||||
}
|
||||
|
||||
private static void RewriteFiles(DirectoryInfo source, ILogger<ConvertCommand> logger)
|
||||
{
|
||||
var files = source.EnumerateFiles("*.cs", SearchOption.AllDirectories);
|
||||
|
||||
var mappings = new Dictionary<string, string>()
|
||||
{
|
||||
// Discard
|
||||
{ "using Windows.UI.Input;", string.Empty },
|
||||
|
||||
// Usings
|
||||
{ "using Windows.UI.Text;", "using Microsoft.UI.Text;\r\nusing Windows.UI.Text;" },
|
||||
{ "using Windows.UI.Xaml.Automation.Peers;", "using Microsoft.UI.Xaml.Automation.Peers;" },
|
||||
{ "using Windows.UI.Xaml.Automation.Provider;", "using Microsoft.UI.Xaml.Automation.Provider;" },
|
||||
{ "using Windows.UI.Xaml.Automation;", "using Microsoft.UI.Xaml.Automation;" },
|
||||
{ "using Windows.UI.Xaml.Controls.Primitives;", "using Microsoft.UI.Xaml.Controls.Primitives;" },
|
||||
{ "using Windows.UI.Xaml.Controls;", "using Microsoft.UI.Xaml.Controls;" },
|
||||
{ "using Windows.UI.Xaml.Data;", "using Microsoft.UI.Xaml.Data;" },
|
||||
{ "using Windows.UI.Xaml.Input;", "using Microsoft.UI.Xaml.Input;" },
|
||||
{ "using Windows.UI.Xaml.Media.Animation;", "using Microsoft.UI.Xaml.Media.Animation;" },
|
||||
{ "using Windows.UI.Xaml.Media;", "using Microsoft.UI.Xaml.Media;" },
|
||||
{ "using Windows.UI.Xaml.Shapes;", "using Microsoft.UI.Xaml.Shapes;" },
|
||||
{ "using Windows.UI.Xaml;", "using Microsoft.UI.Xaml;" },
|
||||
{ "using Windows.UI;", "using Microsoft.UI;" },
|
||||
|
||||
// Namespaces
|
||||
{ "Windows.UI.Xaml.Automation.Peers", "Microsoft.UI.Xaml.Automation.Peers" },
|
||||
{ "Windows.UI.Xaml.Automation", "Microsoft.UI.Xaml.Automation" },
|
||||
{ "Windows.UI.Xaml.Controls", "Microsoft.UI.Xaml.Controls" },
|
||||
};
|
||||
|
||||
var regexes = new Dictionary<string, string>()
|
||||
{
|
||||
// Microsoft.System conflict with System
|
||||
{ "(global::)?System\\.Collections", "global::System.Collections" },
|
||||
{ "(global::)?System\\.ComponentModel", "global::System.ComponentModel" },
|
||||
{ "(global::)?System\\.Globalization", "global::System.Globalization" },
|
||||
{ "(global::)?System\\.Reflection", "global::System.Reflection" },
|
||||
};
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
logger.LogInformation($"Rewriting {file}");
|
||||
|
||||
var content = File.ReadAllText(file.FullName);
|
||||
|
||||
foreach (var mapping in mappings)
|
||||
{
|
||||
content = content.Replace(mapping.Key, mapping.Value);
|
||||
}
|
||||
|
||||
foreach (var regex in regexes)
|
||||
{
|
||||
content = Regex.Replace(content, regex.Key, regex.Value);
|
||||
}
|
||||
|
||||
File.WriteAllText(file.FullName, content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RewriteProjects(DirectoryInfo source, ILogger<ConvertCommand> logger)
|
||||
{
|
||||
var projects = source.EnumerateFiles("*.csproj", SearchOption.AllDirectories);
|
||||
|
||||
foreach (var project in projects)
|
||||
{
|
||||
logger.LogInformation($"Rewriting {project}");
|
||||
|
||||
var document = XDocument.Load(project.FullName);
|
||||
|
||||
document.Root.Attribute("Sdk").Value = "MSBuild.Sdk.Extras/3.0.22";
|
||||
|
||||
document.Root.Descendants("TargetFramework").Single().Value = "net5.0-windows10.0.18362.0";
|
||||
|
||||
var winUIpackageReference = document.Root.Descendants("PackageReference").SingleOrDefault(e => e.Attribute("Include").Value == "Microsoft.WinUI");
|
||||
|
||||
if (winUIpackageReference != null)
|
||||
{
|
||||
winUIpackageReference.Attribute("Version").Value = "3.0.0-preview3.201113.0";
|
||||
}
|
||||
else
|
||||
{
|
||||
document.Root.Add(new XElement("ItemGroup", new XElement("PackageReference", new XAttribute("Include", "Microsoft.WinUI"), new XAttribute("Version", "3.0.0-preview3.201113.0"), null)));
|
||||
}
|
||||
|
||||
using (var xw = XmlWriter.Create(project.FullName, new XmlWriterSettings() { Encoding = Encoding.UTF8, OmitXmlDeclaration = true, Indent = true, NamespaceHandling = NamespaceHandling.OmitDuplicates }))
|
||||
{
|
||||
document.Save(xw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System.CommandLine;
|
||||
using System.CommandLine.Builder;
|
||||
using System.CommandLine.Hosting;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Uno.WinUI3Convert
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static Task<int> Main(string[] args)
|
||||
{
|
||||
var command = new RootCommand("Migrate UWP projects to WinUI 3")
|
||||
{
|
||||
new Argument<DirectoryInfo>("source", "Source directory")
|
||||
{
|
||||
Arity = ArgumentArity.ExactlyOne
|
||||
}
|
||||
.ExistingOnly(),
|
||||
|
||||
new Argument<DirectoryInfo>("destination", "Destination directory")
|
||||
{
|
||||
Arity = ArgumentArity.ExactlyOne
|
||||
},
|
||||
|
||||
new Option<bool>("--overwrite", "Overwrite destination"),
|
||||
};
|
||||
|
||||
command.Handler = CommandHandler.Create<DirectoryInfo, DirectoryInfo, bool, IHost>(ConvertCommand.Execute);
|
||||
|
||||
return new CommandLineBuilder(command)
|
||||
.UseHost(_ => Host.CreateDefaultBuilder())
|
||||
.UseDefaults()
|
||||
.Build()
|
||||
.InvokeAsync(args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Description>A dotnet tool to migrate UWP projects to WinUI 3</Description>
|
||||
<GeneratePackageOnBuild Condition="'$(Configuration)'=='Release'">true</GeneratePackageOnBuild>
|
||||
<PackAsTool>true</PackAsTool>
|
||||
<ToolCommandName>winui3convert</ToolCommandName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.CommandLine.Hosting" Version="0.3.0-alpha.20574.7" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Загрузка…
Ссылка в новой задаче