initial add
|
@ -8,6 +8,7 @@
|
|||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
*.diagsession
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
@ -23,15 +24,13 @@ bld/
|
|||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Gg]enerated*Files/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
@ -45,29 +44,20 @@ TestResult.xml
|
|||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
|
@ -105,9 +95,6 @@ ipch/
|
|||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
|
@ -128,10 +115,6 @@ _TeamCity*
|
|||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
@ -167,7 +150,7 @@ publish/
|
|||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
@ -180,11 +163,11 @@ PublishScripts/
|
|||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
@ -202,7 +185,6 @@ AppPackages/
|
|||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
|
@ -221,10 +203,6 @@ ClientBin/
|
|||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
@ -239,8 +217,6 @@ _UpgradeReport_Files/
|
|||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
|
@ -251,7 +227,6 @@ ServiceFabricBackup/
|
|||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
@ -263,6 +238,9 @@ FakesAssemblies/
|
|||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
|
@ -302,9 +280,6 @@ __pycache__/
|
|||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
|
@ -313,18 +288,3 @@ __pycache__/
|
|||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
#include "pch.h"
|
||||
#include "IntegerToIndentationConverter.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Interop;
|
||||
|
||||
namespace TreeViewControl {
|
||||
|
||||
IntegerToIndentationConverter::IntegerToIndentationConverter()
|
||||
{
|
||||
//default
|
||||
indentMultiplier = 20;
|
||||
}
|
||||
|
||||
Object^ IntegerToIndentationConverter::Convert(Object^ value, TypeName targetType, Object^ parameter, String^ language)
|
||||
{
|
||||
Thickness indent(0);
|
||||
if (value != nullptr)
|
||||
{
|
||||
indent.Left = (int)value * indentMultiplier;
|
||||
return indent;
|
||||
}
|
||||
|
||||
return indent;
|
||||
}
|
||||
|
||||
Object^ IntegerToIndentationConverter::ConvertBack(Object^ value, TypeName targetType, Object^ parameter, String^ language)
|
||||
{
|
||||
throw ref new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
namespace TreeViewControl {
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class IntegerToIndentationConverter sealed : Windows::UI::Xaml::Data::IValueConverter
|
||||
{
|
||||
public:
|
||||
property int IndentMultiplier
|
||||
{
|
||||
int get() { return indentMultiplier; };
|
||||
void set(int i) { indentMultiplier = i; };
|
||||
}
|
||||
|
||||
IntegerToIndentationConverter();
|
||||
|
||||
virtual Platform::Object^ Convert(Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object^ parameter, Platform::String^ language);
|
||||
|
||||
virtual Platform::Object^ ConvertBack(Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object^ parameter, Platform::String^ language);
|
||||
private:
|
||||
int indentMultiplier;
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
#pragma once
|
||||
#include "pch.h"
|
||||
#include "TreeNode.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
using namespace Windows::UI::Xaml::Interop;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
namespace TreeViewControl {
|
||||
|
||||
TreeNode::TreeNode()
|
||||
{
|
||||
childrenVector->VectorChanged += ref new VectorChangedEventHandler<TreeNode ^>(this, &TreeNode::ChildrenVectorChanged);
|
||||
}
|
||||
|
||||
void TreeNode::Append(Object^ value)
|
||||
{
|
||||
int count = childrenVector->Size;
|
||||
TreeNode^ targetNode = (TreeNode^)value;
|
||||
targetNode->ParentNode = this;
|
||||
childrenVector->Append(targetNode);
|
||||
|
||||
//If the count was 0 before we appended, then the HasItems property needs to change.
|
||||
if (count == 0)
|
||||
{
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems"));
|
||||
}
|
||||
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size"));
|
||||
}
|
||||
|
||||
void TreeNode::Clear()
|
||||
{
|
||||
int count = childrenVector->Size;
|
||||
TreeNode^ childNode;
|
||||
for (int i = 0; i < (int)Size; i++)
|
||||
{
|
||||
childNode = (TreeNode^)GetAt(i);
|
||||
childNode->ParentNode = nullptr;
|
||||
}
|
||||
|
||||
childrenVector->Clear();
|
||||
|
||||
//If the count was not 0 before we cleared, then the HasItems property needs to change.
|
||||
if (count != 0)
|
||||
{
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems"));
|
||||
}
|
||||
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size"));
|
||||
}
|
||||
|
||||
IBindableIterator^ TreeNode::First()
|
||||
{
|
||||
return dynamic_cast<IBindableIterator^>(childrenVector->First());
|
||||
}
|
||||
|
||||
Object^ TreeNode::GetAt(unsigned int index)
|
||||
{
|
||||
if ((int)index > -1 && index < childrenVector->Size)
|
||||
{
|
||||
return childrenVector->GetAt(index);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IBindableVectorView^ TreeNode::GetView()
|
||||
{
|
||||
return safe_cast<IBindableVectorView^>(childrenVector->GetView());
|
||||
}
|
||||
|
||||
bool TreeNode::IndexOf(Object^ value, unsigned int* index)
|
||||
{
|
||||
return childrenVector->IndexOf((TreeNode^)value, index);
|
||||
}
|
||||
|
||||
void TreeNode::InsertAt(unsigned int index, Object^ value)
|
||||
{
|
||||
if ((int)index > -1 && index <= childrenVector->Size)
|
||||
{
|
||||
int count = childrenVector->Size;
|
||||
TreeNode^ targetNode = (TreeNode^)value;
|
||||
targetNode->ParentNode = this;
|
||||
return childrenVector->InsertAt(index, (TreeNode^)value);
|
||||
|
||||
//If the count was 0 before we insert, then the HasItems property needs to change.
|
||||
if (count == 0)
|
||||
{
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems"));
|
||||
}
|
||||
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size"));
|
||||
}
|
||||
}
|
||||
|
||||
void TreeNode::RemoveAt(unsigned int index)
|
||||
{
|
||||
if ((int)index > -1 && index < childrenVector->Size)
|
||||
{
|
||||
int count = childrenVector->Size;
|
||||
TreeNode^ targetNode = childrenVector->GetAt(index);
|
||||
targetNode->ParentNode = nullptr;
|
||||
childrenVector->RemoveAt(index);
|
||||
|
||||
//If the count was 1 before we remove, then the HasItems property needs to change.
|
||||
if (count == 1)
|
||||
{
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems"));
|
||||
}
|
||||
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size"));
|
||||
}
|
||||
}
|
||||
|
||||
void TreeNode::RemoveAtEnd()
|
||||
{
|
||||
int count = childrenVector->Size;
|
||||
TreeNode^ targetNode = childrenVector->GetAt(childrenVector->Size - 1);
|
||||
targetNode->ParentNode = nullptr;
|
||||
childrenVector->RemoveAtEnd();
|
||||
|
||||
//If the count was 1 before we remove, then the HasItems property needs to change.
|
||||
if (count == 1)
|
||||
{
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("HasItems"));
|
||||
}
|
||||
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Size"));
|
||||
}
|
||||
|
||||
void TreeNode::SetAt(unsigned int index, Object^ value)
|
||||
{
|
||||
if ((int)index > -1 && index <= childrenVector->Size)
|
||||
{
|
||||
childrenVector->GetAt(index)->ParentNode = nullptr;
|
||||
TreeNode^ targetNode = (TreeNode^)value;
|
||||
targetNode->ParentNode = this;
|
||||
return childrenVector->SetAt(index, targetNode);
|
||||
}
|
||||
}
|
||||
|
||||
void TreeNode::ChildrenVectorChanged(IObservableVector<TreeNode^>^ sender, IVectorChangedEventArgs^ e)
|
||||
{
|
||||
VectorChanged(this, e);
|
||||
}
|
||||
|
||||
unsigned int TreeNode::Size::get()
|
||||
{
|
||||
return childrenVector->Size;
|
||||
}
|
||||
|
||||
Object^ TreeNode::Data::get()
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
void TreeNode::Data::set(Object^ value)
|
||||
{
|
||||
data = value;
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Data"));
|
||||
}
|
||||
|
||||
TreeNode^ TreeNode::ParentNode::get()
|
||||
{
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
void TreeNode::ParentNode::set(TreeNode^ value)
|
||||
{
|
||||
parentNode = value;
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("ParentNode"));
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("Depth"));
|
||||
}
|
||||
|
||||
bool TreeNode::IsExpanded::get()
|
||||
{
|
||||
return isExpanded;
|
||||
}
|
||||
|
||||
void TreeNode::IsExpanded::set(bool value)
|
||||
{
|
||||
isExpanded = value;
|
||||
this->PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("IsExpanded"));
|
||||
}
|
||||
|
||||
bool TreeNode::HasItems::get()
|
||||
{
|
||||
return (Size != 0);
|
||||
}
|
||||
|
||||
int TreeNode::Depth::get()
|
||||
{
|
||||
TreeNode^ ancestorNode = this;
|
||||
int depth = -1;
|
||||
while ((ancestorNode->ParentNode) != nullptr)
|
||||
{
|
||||
depth++;
|
||||
ancestorNode = ancestorNode->ParentNode;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
#pragma once
|
||||
|
||||
namespace TreeViewControl {
|
||||
/// <summary>
|
||||
/// The TreeNode class implements the hierarchical layout for the TreeView.
|
||||
/// It also holds the data that will be bound to in the item template.
|
||||
/// </summary>
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class TreeNode sealed : Windows::UI::Xaml::Interop::IBindableObservableVector, Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
TreeNode();
|
||||
|
||||
virtual void Append(Object^ value);
|
||||
|
||||
virtual void Clear();
|
||||
|
||||
virtual Windows::UI::Xaml::Interop::IBindableIterator^ First();
|
||||
|
||||
virtual Object^ GetAt(unsigned int index);
|
||||
|
||||
virtual Windows::UI::Xaml::Interop::IBindableVectorView^ GetView();
|
||||
|
||||
virtual bool IndexOf(Object^ value, unsigned int* index);
|
||||
|
||||
virtual void InsertAt(unsigned int index, Object^ value);
|
||||
|
||||
virtual void RemoveAt(unsigned int index);
|
||||
|
||||
virtual void RemoveAtEnd();
|
||||
|
||||
virtual void SetAt(unsigned int index, Object^ value);
|
||||
|
||||
virtual event Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ VectorChanged
|
||||
{
|
||||
virtual Windows::Foundation::EventRegistrationToken add(Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ e)
|
||||
{
|
||||
return TreeNodeChanged += e;
|
||||
}
|
||||
|
||||
virtual void remove(Windows::Foundation::EventRegistrationToken t)
|
||||
{
|
||||
TreeNodeChanged -= t;
|
||||
}
|
||||
|
||||
internal: virtual void raise(Windows::UI::Xaml::Interop::IBindableObservableVector^ vector, Platform::Object^ e)
|
||||
{
|
||||
TreeNodeChanged(vector, e);
|
||||
}
|
||||
}
|
||||
|
||||
virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
|
||||
|
||||
virtual property unsigned int Size
|
||||
{
|
||||
unsigned int get();
|
||||
}
|
||||
|
||||
property Object^ Data
|
||||
{
|
||||
Object^ get();
|
||||
void set(Object^ value);
|
||||
}
|
||||
|
||||
property TreeNode^ ParentNode
|
||||
{
|
||||
TreeNode^ get();
|
||||
void set(TreeNode^ value);
|
||||
}
|
||||
|
||||
property bool IsExpanded
|
||||
{
|
||||
bool get();
|
||||
void set(bool value);
|
||||
}
|
||||
|
||||
property bool HasItems
|
||||
{
|
||||
bool get();
|
||||
}
|
||||
|
||||
//A lone TreeNode will have a depth of -1, this is to show that it is not appended
|
||||
//under the TreeView's invisible root node. Once added into the TreeView via
|
||||
//that method, the depth of the node will be calculated appropriately.
|
||||
property int Depth
|
||||
{
|
||||
int get();
|
||||
}
|
||||
|
||||
event Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ TreeNodeChanged;
|
||||
|
||||
private:
|
||||
TreeNode^ parentNode = nullptr;
|
||||
Object^ data = nullptr;
|
||||
bool isExpanded = false;
|
||||
Platform::Collections::Vector<TreeNode^>^ childrenVector = ref new Platform::Collections::Vector<TreeNode^>();
|
||||
void ChildrenVectorChanged(Windows::Foundation::Collections::IObservableVector<TreeNode^>^ sender, Windows::Foundation::Collections::IVectorChangedEventArgs^ e);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
#pragma once
|
||||
#include "pch.h"
|
||||
#include "TreeView.h"
|
||||
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Interop;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
namespace TreeViewControl {
|
||||
|
||||
TreeView::TreeView()
|
||||
{
|
||||
flatViewModel = ref new ViewModel;
|
||||
rootNode = ref new TreeNode();
|
||||
|
||||
flatViewModel->ExpandNode(rootNode);
|
||||
|
||||
CanReorderItems = true;
|
||||
AllowDrop = true;
|
||||
CanDragItems = true;
|
||||
|
||||
rootNode->VectorChanged += ref new BindableVectorChangedEventHandler(flatViewModel, &ViewModel::TreeNodeVectorChanged);
|
||||
ItemClick += ref new Windows::UI::Xaml::Controls::ItemClickEventHandler(this, &TreeView::TreeView_OnItemClick);
|
||||
DragItemsStarting += ref new Windows::UI::Xaml::Controls::DragItemsStartingEventHandler(this, &TreeView::TreeView_DragItemsStarting);
|
||||
DragItemsCompleted += ref new Windows::Foundation::TypedEventHandler<Windows::UI::Xaml::Controls::ListViewBase ^, Windows::UI::Xaml::Controls::DragItemsCompletedEventArgs ^>(this, &TreeView::TreeView_DragItemsCompleted);
|
||||
ItemsSource = flatViewModel;
|
||||
}
|
||||
|
||||
void TreeView::TreeView_OnItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ args)
|
||||
{
|
||||
TreeViewItemClickEventArgs^ treeViewArgs = ref new TreeViewItemClickEventArgs();
|
||||
treeViewArgs->ClickedItem = args->ClickedItem;
|
||||
|
||||
TreeViewItemClick(this, treeViewArgs);
|
||||
|
||||
if (!treeViewArgs->IsHandled)
|
||||
{
|
||||
TreeNode^ targetNode = (TreeNode^)args->ClickedItem;
|
||||
if (targetNode->IsExpanded)
|
||||
{
|
||||
flatViewModel->CollapseNode(targetNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
flatViewModel->ExpandNode(targetNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreeView::TreeView_DragItemsStarting(Platform::Object^ sender, Windows::UI::Xaml::Controls::DragItemsStartingEventArgs^ e)
|
||||
{
|
||||
draggedTreeViewItem = (TreeViewItem^)this->ContainerFromItem(e->Items->GetAt(0));
|
||||
}
|
||||
|
||||
void TreeView::TreeView_DragItemsCompleted(Windows::UI::Xaml::Controls::ListViewBase^ sender, Windows::UI::Xaml::Controls::DragItemsCompletedEventArgs^ args)
|
||||
{
|
||||
draggedTreeViewItem = nullptr;
|
||||
}
|
||||
|
||||
void TreeView::OnDrop(Windows::UI::Xaml::DragEventArgs^ e)
|
||||
{
|
||||
if (e->AcceptedOperation == Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move)
|
||||
{
|
||||
Panel^ panel = this->ItemsPanelRoot;
|
||||
Windows::Foundation::Point point = e->GetPosition(panel);
|
||||
|
||||
int aboveIndex = -1;
|
||||
int belowIndex = -1;
|
||||
unsigned int relativeIndex;
|
||||
|
||||
IInsertionPanel^ insertionPanel = (IInsertionPanel^)panel;
|
||||
|
||||
if (insertionPanel != nullptr)
|
||||
{
|
||||
insertionPanel->GetInsertionIndexes(point, &aboveIndex, &belowIndex);
|
||||
|
||||
TreeNode^ aboveNode = (TreeNode^)flatViewModel->GetAt(aboveIndex);
|
||||
TreeNode^ belowNode = (TreeNode^)flatViewModel->GetAt(belowIndex);
|
||||
TreeNode^ targetNode = (TreeNode^)this->ItemFromContainer(draggedTreeViewItem);
|
||||
|
||||
//Between two items
|
||||
if (aboveNode && belowNode)
|
||||
{
|
||||
targetNode->ParentNode->IndexOf(targetNode, &relativeIndex);
|
||||
targetNode->ParentNode->RemoveAt(relativeIndex);
|
||||
|
||||
if (belowNode->ParentNode == aboveNode)
|
||||
{
|
||||
aboveNode->InsertAt(0, targetNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
aboveNode->ParentNode->IndexOf(aboveNode, &relativeIndex);
|
||||
aboveNode->ParentNode->InsertAt(relativeIndex + 1, targetNode);
|
||||
}
|
||||
}
|
||||
//Bottom of the list
|
||||
else if (aboveNode && !belowNode)
|
||||
{
|
||||
targetNode->ParentNode->IndexOf(targetNode, &relativeIndex);
|
||||
targetNode->ParentNode->RemoveAt(relativeIndex);
|
||||
|
||||
aboveNode->ParentNode->IndexOf(aboveNode, &relativeIndex);
|
||||
aboveNode->ParentNode->InsertAt(relativeIndex + 1, targetNode);
|
||||
}
|
||||
//Top of the list
|
||||
else if (!aboveNode && belowNode)
|
||||
{
|
||||
targetNode->ParentNode->IndexOf(targetNode, &relativeIndex);
|
||||
targetNode->ParentNode->RemoveAt(relativeIndex);
|
||||
|
||||
rootNode->InsertAt(0, targetNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e->Handled = true;
|
||||
ListViewBase::OnDrop(e);
|
||||
}
|
||||
|
||||
void TreeView::OnDragOver(Windows::UI::Xaml::DragEventArgs^ e)
|
||||
{
|
||||
Windows::ApplicationModel::DataTransfer::DataPackageOperation savedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None;
|
||||
|
||||
e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None;
|
||||
|
||||
Panel^ panel = this->ItemsPanelRoot;
|
||||
Windows::Foundation::Point point = e->GetPosition(panel);
|
||||
|
||||
int aboveIndex = -1;
|
||||
int belowIndex = -1;
|
||||
|
||||
IInsertionPanel^ insertionPanel = (IInsertionPanel^)panel;
|
||||
|
||||
if (insertionPanel != nullptr)
|
||||
{
|
||||
insertionPanel->GetInsertionIndexes(point, &aboveIndex, &belowIndex);
|
||||
|
||||
if (aboveIndex > -1)
|
||||
{
|
||||
TreeNode^ aboveNode = (TreeNode^)flatViewModel->GetAt(aboveIndex);
|
||||
TreeNode^ targetNode = (TreeNode^)this->ItemFromContainer(draggedTreeViewItem);
|
||||
|
||||
TreeNode^ ancestorNode = aboveNode;
|
||||
while (ancestorNode != nullptr && ancestorNode != targetNode)
|
||||
{
|
||||
ancestorNode = ancestorNode->ParentNode;
|
||||
}
|
||||
|
||||
if (ancestorNode == nullptr)
|
||||
{
|
||||
savedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move;
|
||||
e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
savedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move;
|
||||
e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move;
|
||||
}
|
||||
}
|
||||
|
||||
ListViewBase::OnDragOver(e);
|
||||
e->AcceptedOperation = savedOperation;
|
||||
}
|
||||
|
||||
void TreeView::ExpandNode(TreeNode^ targetNode)
|
||||
{
|
||||
flatViewModel->ExpandNode(targetNode);
|
||||
}
|
||||
|
||||
void TreeView::CollapseNode(TreeNode^ targetNode)
|
||||
{
|
||||
flatViewModel->CollapseNode(targetNode);
|
||||
}
|
||||
|
||||
void TreeView::PrepareContainerForItemOverride(DependencyObject^ element, Object^ item)
|
||||
{
|
||||
((UIElement^)element)->AllowDrop = true;
|
||||
|
||||
ListView::PrepareContainerForItemOverride(element, item);
|
||||
}
|
||||
|
||||
DependencyObject^ TreeView::GetContainerForItemOverride()
|
||||
{
|
||||
TreeViewItem^ targetItem = ref new TreeViewItem();
|
||||
return (DependencyObject^)targetItem;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
#include "TreeNode.h"
|
||||
#include "ViewModel.h"
|
||||
#include "TreeViewItem.h"
|
||||
|
||||
namespace TreeViewControl {
|
||||
public ref class TreeViewItemClickEventArgs sealed
|
||||
{
|
||||
public:
|
||||
TreeViewItemClickEventArgs() {}
|
||||
|
||||
property Object^ ClickedItem
|
||||
{
|
||||
Object^ get() { return clickedItem; };
|
||||
void set(Object^ value) { clickedItem = value; };
|
||||
}
|
||||
|
||||
property bool IsHandled
|
||||
{
|
||||
bool get() { return isHandled; };
|
||||
void set(bool value) { isHandled = value; };
|
||||
}
|
||||
private:
|
||||
Object^ clickedItem = nullptr;
|
||||
bool isHandled = false;
|
||||
};
|
||||
|
||||
ref class TreeView;
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public delegate void TreeViewItemClickHandler(TreeView^ sender, TreeViewItemClickEventArgs^ args);
|
||||
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class TreeView sealed : Windows::UI::Xaml::Controls::ListView
|
||||
{
|
||||
public:
|
||||
TreeView();
|
||||
|
||||
//This event is used to expose an alternative to itemclick to developers.
|
||||
event TreeViewItemClickHandler^ TreeViewItemClick;
|
||||
|
||||
//This RootNode property is used by the TreeView to handle additions into the TreeView and
|
||||
//accurate VectorChange with multiple 'root level nodes'. This node will not be placed
|
||||
//in the flatViewModel, but has it's vectorchanged event hooked up to flatViewModel's
|
||||
//handler.
|
||||
property TreeNode^ RootNode
|
||||
{
|
||||
TreeNode^ get() { return rootNode; };
|
||||
}
|
||||
|
||||
void TreeView_OnItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ args);
|
||||
|
||||
void TreeView_DragItemsStarting(Platform::Object^ sender, Windows::UI::Xaml::Controls::DragItemsStartingEventArgs^ e);
|
||||
|
||||
void TreeView_DragItemsCompleted(Windows::UI::Xaml::Controls::ListViewBase^ sender, Windows::UI::Xaml::Controls::DragItemsCompletedEventArgs^ args);
|
||||
|
||||
void ExpandNode(TreeNode^ targetNode);
|
||||
|
||||
void CollapseNode(TreeNode^ targetNode);
|
||||
|
||||
protected:
|
||||
void PrepareContainerForItemOverride(DependencyObject^ element, Object^ item) override;
|
||||
Windows::UI::Xaml::DependencyObject^ GetContainerForItemOverride() override;
|
||||
|
||||
void OnDrop(Windows::UI::Xaml::DragEventArgs^ e) override;
|
||||
void OnDragOver(Windows::UI::Xaml::DragEventArgs^ e) override;
|
||||
|
||||
private:
|
||||
TreeNode^ rootNode;
|
||||
ViewModel^ flatViewModel;
|
||||
|
||||
internal:
|
||||
TreeViewItem^ draggedTreeViewItem;
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{bdc228d2-7929-5a98-b7ba-5e28bd1adbcf}</ProjectGuid>
|
||||
<Keyword>WindowsRuntimeComponent</Keyword>
|
||||
<ProjectName>TreeViewControl</ProjectName>
|
||||
<RootNamespace>TreeViewControl</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.16299.0</WindowsTargetPlatformMinVersion>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="IntegerToIndentationConverter.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="TreeNode.h" />
|
||||
<ClInclude Include="TreeView.h" />
|
||||
<ClInclude Include="TreeViewItem.h" />
|
||||
<ClInclude Include="TreeViewItemAutomationPeer.h" />
|
||||
<ClInclude Include="ViewModel.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="IntegerToIndentationConverter.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TreeNode.cpp" />
|
||||
<ClCompile Include="TreeView.cpp" />
|
||||
<ClCompile Include="TreeViewItem.cpp" />
|
||||
<ClCompile Include="TreeViewItemAutomationPeer.cpp" />
|
||||
<ClCompile Include="ViewModel.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Resources">
|
||||
<UniqueIdentifier>96c875b2-800f-42e2-aba6-ef897e02ec12</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="TreeNode.cpp" />
|
||||
<ClCompile Include="TreeView.cpp" />
|
||||
<ClCompile Include="TreeViewItem.cpp" />
|
||||
<ClCompile Include="TreeViewItemAutomationPeer.cpp" />
|
||||
<ClCompile Include="ViewModel.cpp" />
|
||||
<ClCompile Include="IntegerToIndentationConverter.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="TreeNode.h" />
|
||||
<ClInclude Include="TreeView.h" />
|
||||
<ClInclude Include="TreeViewItem.h" />
|
||||
<ClInclude Include="TreeViewItemAutomationPeer.h" />
|
||||
<ClInclude Include="ViewModel.h" />
|
||||
<ClInclude Include="IntegerToIndentationConverter.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,127 @@
|
|||
#pragma once
|
||||
#include "pch.h"
|
||||
#include "TreeViewItem.h"
|
||||
#include "TreeViewItemAutomationPeer.h"
|
||||
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Platform;
|
||||
|
||||
namespace TreeViewControl {
|
||||
TreeViewItem::TreeViewItem()
|
||||
{
|
||||
}
|
||||
|
||||
TreeViewItem::~TreeViewItem()
|
||||
{
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Controls::ListView^ TreeViewItem::GetAncestorListView(TreeViewItem^ targetItem)
|
||||
{
|
||||
DependencyObject^ TreeViewItemAncestor = (DependencyObject^)this;
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = nullptr;
|
||||
while (TreeViewItemAncestor != nullptr && ancestorListView == nullptr)
|
||||
{
|
||||
TreeViewItemAncestor = Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(TreeViewItemAncestor);
|
||||
ancestorListView = dynamic_cast<Windows::UI::Xaml::Controls::ListView^>(TreeViewItemAncestor);
|
||||
}
|
||||
return ancestorListView;
|
||||
}
|
||||
|
||||
void TreeViewItem::OnDrop(Windows::UI::Xaml::DragEventArgs^ e)
|
||||
{
|
||||
if (e->AcceptedOperation == Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move)
|
||||
{
|
||||
TreeViewItem^ droppedOnItem = (TreeViewItem^)this;
|
||||
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetAncestorListView(droppedOnItem);
|
||||
|
||||
if (ancestorListView)
|
||||
{
|
||||
TreeView^ ancestorTreeView = (TreeView^)ancestorListView;
|
||||
TreeViewItem^ droppedItem = ancestorTreeView->draggedTreeViewItem;
|
||||
TreeNode^ droppedNode = (TreeNode^)ancestorTreeView->ItemFromContainer(droppedItem);
|
||||
TreeNode^ droppedOnNode = (TreeNode^)ancestorTreeView->ItemFromContainer(droppedOnItem);
|
||||
|
||||
//Remove the item that was dragged
|
||||
unsigned int removeIndex;
|
||||
droppedNode->ParentNode->IndexOf(droppedNode, &removeIndex);
|
||||
|
||||
if (droppedNode != droppedOnNode)
|
||||
{
|
||||
droppedNode->ParentNode->RemoveAt(removeIndex);
|
||||
|
||||
//Append the dragged dropped item as a child of the node it was dropped onto
|
||||
droppedOnNode->Append(droppedNode);
|
||||
|
||||
//If not set to true then the Reorder code of listview wil override what is being done here.
|
||||
e->Handled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreeViewItem::OnDragEnter(Windows::UI::Xaml::DragEventArgs^ e)
|
||||
{
|
||||
TreeViewItem^ draggedOverItem = (TreeViewItem^)this;
|
||||
|
||||
e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None;
|
||||
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetAncestorListView(draggedOverItem);
|
||||
|
||||
if (ancestorListView)
|
||||
{
|
||||
TreeView^ ancestorTreeView = (TreeView^)ancestorListView;
|
||||
TreeViewItem^ draggedTreeViewItem = ancestorTreeView->draggedTreeViewItem;
|
||||
TreeNode^ draggedNode = (TreeNode^)ancestorTreeView->ItemFromContainer(draggedTreeViewItem);
|
||||
TreeNode^ draggedOverNode = (TreeNode^)ancestorTreeView->ItemFromContainer(draggedOverItem);
|
||||
TreeNode^ walkNode = draggedOverNode->ParentNode;
|
||||
|
||||
while (walkNode != nullptr && walkNode != draggedNode)
|
||||
{
|
||||
walkNode = walkNode->ParentNode;
|
||||
}
|
||||
|
||||
if (walkNode != draggedNode && draggedNode != draggedOverNode)
|
||||
{
|
||||
e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreeViewItem::OnDragOver(Windows::UI::Xaml::DragEventArgs^ e)
|
||||
{
|
||||
e->DragUIOverride->IsGlyphVisible = true;
|
||||
e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::None;
|
||||
|
||||
TreeViewItem^ draggedOverItem = (TreeViewItem^)this;
|
||||
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetAncestorListView(draggedOverItem);
|
||||
if (ancestorListView)
|
||||
{
|
||||
TreeView^ ancestorTreeView = (TreeView^)ancestorListView;
|
||||
TreeViewItem^ draggedTreeViewItem = ancestorTreeView->draggedTreeViewItem;
|
||||
TreeNode^ draggedNode = (TreeNode^)ancestorTreeView->ItemFromContainer(draggedTreeViewItem);
|
||||
TreeNode^ draggedOverNode = (TreeNode^)ancestorTreeView->ItemFromContainer(draggedOverItem);
|
||||
TreeNode^ walkNode = draggedOverNode->ParentNode;
|
||||
|
||||
while (walkNode != nullptr && walkNode != draggedNode)
|
||||
{
|
||||
walkNode = walkNode->ParentNode;
|
||||
}
|
||||
|
||||
if (walkNode != draggedNode && draggedNode != draggedOverNode)
|
||||
{
|
||||
e->AcceptedOperation = Windows::ApplicationModel::DataTransfer::DataPackageOperation::Move;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Automation::Peers::AutomationPeer^ TreeViewItem::OnCreateAutomationPeer()
|
||||
{
|
||||
return ref new TreeViewItemAutomationPeer(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
namespace TreeViewControl {
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class TreeViewItem sealed : Windows::UI::Xaml::Controls::ListViewItem
|
||||
{
|
||||
public:
|
||||
TreeViewItem();
|
||||
|
||||
virtual ~TreeViewItem();
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::Controls::ListView^ GetAncestorListView(TreeViewItem^ targetItem);
|
||||
|
||||
protected:
|
||||
void OnDrop(Windows::UI::Xaml::DragEventArgs^ e) override;
|
||||
void OnDragEnter(Windows::UI::Xaml::DragEventArgs^ e) override;
|
||||
void OnDragOver(Windows::UI::Xaml::DragEventArgs^ e) override;
|
||||
Windows::UI::Xaml::Automation::Peers::AutomationPeer^ OnCreateAutomationPeer() override;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
#pragma once
|
||||
#include "pch.h"
|
||||
#include "TreeViewItemAutomationPeer.h"
|
||||
#include "TreeNode.h"
|
||||
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
|
||||
|
||||
namespace TreeViewControl {
|
||||
//IExpandCollapseProvider
|
||||
Windows::UI::Xaml::Automation::ExpandCollapseState TreeViewItemAutomationPeer::ExpandCollapseState::get()
|
||||
{
|
||||
Windows::UI::Xaml::Automation::ExpandCollapseState currentState = Windows::UI::Xaml::Automation::ExpandCollapseState::Collapsed;
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner);
|
||||
|
||||
TreeNode^ targetNode;
|
||||
TreeNode^ targetParentNode;
|
||||
|
||||
if (ancestorListView)
|
||||
{
|
||||
TreeView^ ancestorTreeView = (TreeView^)ancestorListView;
|
||||
targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner);
|
||||
|
||||
if (Owner->AllowDrop)
|
||||
{
|
||||
if (targetNode->IsExpanded)
|
||||
{
|
||||
currentState = Windows::UI::Xaml::Automation::ExpandCollapseState::Expanded;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = Windows::UI::Xaml::Automation::ExpandCollapseState::Collapsed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = Windows::UI::Xaml::Automation::ExpandCollapseState::LeafNode;
|
||||
}
|
||||
}
|
||||
|
||||
return currentState;
|
||||
}
|
||||
|
||||
void TreeViewItemAutomationPeer::Collapse()
|
||||
{
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner);
|
||||
|
||||
if (ancestorListView)
|
||||
{
|
||||
TreeView^ ancestorTreeView = (TreeView^)ancestorListView;
|
||||
TreeNode^ targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner);
|
||||
ancestorTreeView->CollapseNode(targetNode);
|
||||
RaiseExpandCollapseAutomationEvent(Windows::UI::Xaml::Automation::ExpandCollapseState::Collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void TreeViewItemAutomationPeer::Expand()
|
||||
{
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner);
|
||||
|
||||
if (ancestorListView)
|
||||
{
|
||||
TreeView^ ancestorTreeView = (TreeView^)ancestorListView;
|
||||
TreeNode^ targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner);
|
||||
ancestorTreeView->ExpandNode(targetNode);
|
||||
RaiseExpandCollapseAutomationEvent(Windows::UI::Xaml::Automation::ExpandCollapseState::Expanded);
|
||||
}
|
||||
}
|
||||
|
||||
void TreeViewItemAutomationPeer::RaiseExpandCollapseAutomationEvent(Windows::UI::Xaml::Automation::ExpandCollapseState newState)
|
||||
{
|
||||
Windows::UI::Xaml::Automation::ExpandCollapseState oldState;
|
||||
|
||||
if (newState == Windows::UI::Xaml::Automation::ExpandCollapseState::Expanded)
|
||||
{
|
||||
oldState = Windows::UI::Xaml::Automation::ExpandCollapseState::Collapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
oldState = Windows::UI::Xaml::Automation::ExpandCollapseState::Expanded;
|
||||
}
|
||||
|
||||
RaisePropertyChangedEvent(ExpandCollapsePatternIdentifiers::ExpandCollapseStateProperty, oldState, newState);
|
||||
}
|
||||
|
||||
//Position override
|
||||
|
||||
//These methods are being overridden so that the TreeView under narrator reads out
|
||||
//the position of an item as compared to it's children, not it's overall position
|
||||
//in the listview. I've included an override for level as well, to give context on
|
||||
//how deep in the tree an item is.
|
||||
int TreeViewItemAutomationPeer::GetSizeOfSetCore()
|
||||
{
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner);
|
||||
|
||||
TreeNode^ targetNode;
|
||||
TreeNode^ targetParentNode;
|
||||
|
||||
int setSize = 0;
|
||||
|
||||
if (ancestorListView)
|
||||
{
|
||||
TreeView^ ancestorTreeView = (TreeView^)ancestorListView;
|
||||
targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner);
|
||||
targetParentNode = targetNode->ParentNode;
|
||||
setSize = targetParentNode->Size;
|
||||
}
|
||||
|
||||
return setSize;
|
||||
}
|
||||
|
||||
int TreeViewItemAutomationPeer::GetPositionInSetCore()
|
||||
{
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner);
|
||||
|
||||
TreeNode^ targetNode;
|
||||
TreeNode^ targetParentNode;
|
||||
|
||||
int positionInSet = 0;
|
||||
|
||||
if (ancestorListView)
|
||||
{
|
||||
TreeView^ ancestorTreeView = (TreeView^)ancestorListView;
|
||||
targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner);
|
||||
unsigned int positionInt;
|
||||
targetParentNode = targetNode->ParentNode;
|
||||
targetParentNode->IndexOf(targetNode, &positionInt);
|
||||
positionInSet = (int)positionInt + 1;
|
||||
}
|
||||
|
||||
return positionInSet;
|
||||
}
|
||||
|
||||
int TreeViewItemAutomationPeer::GetLevelCore()
|
||||
{
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = GetParentListView((DependencyObject^)Owner);
|
||||
|
||||
TreeNode^ targetNode;
|
||||
TreeNode^ targetParentNode;
|
||||
|
||||
int levelValue = 0;
|
||||
|
||||
if (ancestorListView)
|
||||
{
|
||||
TreeView^ ancestorTreeView = (TreeView^)ancestorListView;
|
||||
targetNode = (TreeNode^)ancestorTreeView->ItemFromContainer((TreeViewItem^)Owner);
|
||||
levelValue = targetNode->Depth + 1;
|
||||
}
|
||||
|
||||
return levelValue;
|
||||
}
|
||||
|
||||
Platform::Object^ TreeViewItemAutomationPeer::GetPatternCore(Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface)
|
||||
{
|
||||
if (patternInterface == Windows::UI::Xaml::Automation::Peers::PatternInterface::ExpandCollapse)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return ListViewItemAutomationPeer::GetPatternCore(patternInterface);
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Controls::ListView^ TreeViewItemAutomationPeer::GetParentListView(DependencyObject^ Owner)
|
||||
{
|
||||
DependencyObject^ treeViewItemAncestor = Owner;
|
||||
Windows::UI::Xaml::Controls::ListView^ ancestorListView = nullptr;
|
||||
while (treeViewItemAncestor != nullptr && ancestorListView == nullptr)
|
||||
{
|
||||
treeViewItemAncestor = Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(treeViewItemAncestor);
|
||||
ancestorListView = dynamic_cast<Windows::UI::Xaml::Controls::ListView^>(treeViewItemAncestor);
|
||||
}
|
||||
|
||||
return ancestorListView;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
#include "TreeViewItem.h"
|
||||
#include "TreeView.h"
|
||||
|
||||
namespace TreeViewControl {
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class TreeViewItemAutomationPeer : Windows::UI::Xaml::Automation::Peers::ListViewItemAutomationPeer, Windows::UI::Xaml::Automation::Provider::IExpandCollapseProvider
|
||||
{
|
||||
internal:
|
||||
TreeViewItemAutomationPeer(TreeViewItem^ owner) :Windows::UI::Xaml::Automation::Peers::ListViewItemAutomationPeer(owner) {};
|
||||
|
||||
public:
|
||||
//IExpandCollapseProvider
|
||||
virtual void Collapse();
|
||||
|
||||
virtual void Expand();
|
||||
|
||||
property Windows::UI::Xaml::Automation::ExpandCollapseState ExpandCollapseState
|
||||
{
|
||||
virtual Windows::UI::Xaml::Automation::ExpandCollapseState get();
|
||||
}
|
||||
|
||||
void RaiseExpandCollapseAutomationEvent(Windows::UI::Xaml::Automation::ExpandCollapseState newState);
|
||||
|
||||
//Position override
|
||||
int GetSizeOfSetCore() override;
|
||||
|
||||
int GetPositionInSetCore() override;
|
||||
|
||||
int GetLevelCore() override;
|
||||
|
||||
protected:
|
||||
Platform::Object^ GetPatternCore(Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface) override;
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::Controls::ListView^ GetParentListView(DependencyObject^ Owner);
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,450 @@
|
|||
#pragma once
|
||||
#include "pch.h"
|
||||
#include "ViewModel.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::UI::Xaml::Interop;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
|
||||
namespace TreeViewControl {
|
||||
ViewModel::ViewModel()
|
||||
{
|
||||
flatVectorRealizedItems->VectorChanged += ref new VectorChangedEventHandler<TreeNode ^>(this, &ViewModel::UpdateTreeView);
|
||||
}
|
||||
|
||||
void ViewModel::Append(Object^ value)
|
||||
{
|
||||
TreeNode^ targetNode = (TreeNode^)value;
|
||||
flatVectorRealizedItems->Append(targetNode);
|
||||
|
||||
collectionChangedEventTokenVector.push_back(targetNode->VectorChanged += ref new BindableVectorChangedEventHandler(this, &ViewModel::TreeNodeVectorChanged));
|
||||
propertyChangedEventTokenVector.push_back(targetNode->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &ViewModel::TreeNodePropertyChanged));
|
||||
}
|
||||
|
||||
void ViewModel::Clear()
|
||||
{
|
||||
|
||||
while (flatVectorRealizedItems->Size != 0)
|
||||
{
|
||||
RemoveAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
IBindableIterator^ ViewModel::First()
|
||||
{
|
||||
return dynamic_cast<IBindableIterator^>(flatVectorRealizedItems->First());
|
||||
}
|
||||
|
||||
Object^ ViewModel::GetAt(unsigned int index)
|
||||
{
|
||||
if ((int)index > -1 && (int)index < flatVectorRealizedItems->Size)
|
||||
{
|
||||
return flatVectorRealizedItems->GetAt(index);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IBindableVectorView^ ViewModel::GetView()
|
||||
{
|
||||
return safe_cast<IBindableVectorView^>(flatVectorRealizedItems->GetView());
|
||||
}
|
||||
|
||||
bool ViewModel::IndexOf(Object^ value, unsigned int* index)
|
||||
{
|
||||
return flatVectorRealizedItems->IndexOf((TreeNode^)value, index);
|
||||
}
|
||||
|
||||
void ViewModel::InsertAt(unsigned int index, Object^ value)
|
||||
{
|
||||
if ((int)index > -1 && (int)index <= flatVectorRealizedItems->Size)
|
||||
{
|
||||
TreeNode^ targetNode = (TreeNode^)value;
|
||||
flatVectorRealizedItems->InsertAt(index, targetNode);
|
||||
|
||||
auto eventIndex = collectionChangedEventTokenVector.begin() + index;
|
||||
collectionChangedEventTokenVector.insert(eventIndex, targetNode->VectorChanged += ref new BindableVectorChangedEventHandler(this, &ViewModel::TreeNodeVectorChanged));
|
||||
|
||||
eventIndex = propertyChangedEventTokenVector.begin() + index;
|
||||
propertyChangedEventTokenVector.insert(eventIndex,targetNode->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &ViewModel::TreeNodePropertyChanged));
|
||||
}
|
||||
}
|
||||
|
||||
void ViewModel::RemoveAt(unsigned int index)
|
||||
{
|
||||
if ((int)index > -1 && (int)index < flatVectorRealizedItems->Size)
|
||||
{
|
||||
TreeNode^ targetNode = flatVectorRealizedItems->GetAt(index);
|
||||
flatVectorRealizedItems->RemoveAt(index);
|
||||
|
||||
auto eventIndex = collectionChangedEventTokenVector.begin() + index;
|
||||
targetNode->VectorChanged -= collectionChangedEventTokenVector[index];
|
||||
collectionChangedEventTokenVector.erase(eventIndex);
|
||||
|
||||
eventIndex = propertyChangedEventTokenVector.begin() + index;
|
||||
targetNode->PropertyChanged -= propertyChangedEventTokenVector[index];
|
||||
propertyChangedEventTokenVector.erase(eventIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewModel::RemoveAtEnd()
|
||||
{
|
||||
int index = flatVectorRealizedItems->Size - 1;
|
||||
if (index >= 0)
|
||||
{
|
||||
TreeNode^ targetNode = flatVectorRealizedItems->GetAt(index);
|
||||
flatVectorRealizedItems->RemoveAt(index);
|
||||
|
||||
auto eventIndex = collectionChangedEventTokenVector.begin() + index;
|
||||
targetNode->VectorChanged -= collectionChangedEventTokenVector[index];
|
||||
collectionChangedEventTokenVector.erase(eventIndex);
|
||||
|
||||
eventIndex = propertyChangedEventTokenVector.begin() + index;
|
||||
targetNode->PropertyChanged -= propertyChangedEventTokenVector[index];
|
||||
propertyChangedEventTokenVector.erase(eventIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewModel::SetAt(unsigned int index, Object^ value)
|
||||
{
|
||||
if ((int)index > -1 && (int)index < flatVectorRealizedItems->Size)
|
||||
{
|
||||
TreeNode^ targetNode = (TreeNode^)value;
|
||||
TreeNode^ removeNode = flatVectorRealizedItems->GetAt(index);
|
||||
flatVectorRealizedItems->SetAt(index, targetNode);
|
||||
|
||||
auto eventIndex = collectionChangedEventTokenVector.begin() + index;
|
||||
removeNode->VectorChanged -= collectionChangedEventTokenVector[index];
|
||||
collectionChangedEventTokenVector.erase(eventIndex);
|
||||
collectionChangedEventTokenVector.insert(eventIndex, targetNode->VectorChanged += ref new BindableVectorChangedEventHandler(this, &ViewModel::TreeNodeVectorChanged));
|
||||
|
||||
eventIndex = propertyChangedEventTokenVector.begin() + index;
|
||||
targetNode->PropertyChanged -= propertyChangedEventTokenVector[index];
|
||||
propertyChangedEventTokenVector.erase(eventIndex);
|
||||
propertyChangedEventTokenVector.insert(eventIndex, targetNode->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &ViewModel::TreeNodePropertyChanged));
|
||||
}
|
||||
}
|
||||
|
||||
void ViewModel::ExpandNode(TreeNode^ targetNode)
|
||||
{
|
||||
if (!targetNode->IsExpanded)
|
||||
{
|
||||
targetNode->IsExpanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewModel::CollapseNode(TreeNode^ targetNode)
|
||||
{
|
||||
if (targetNode->IsExpanded)
|
||||
{
|
||||
targetNode->IsExpanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewModel::AddNodeToView(TreeNode^ targetNode, int index)
|
||||
{
|
||||
InsertAt(index, targetNode);
|
||||
}
|
||||
|
||||
int ViewModel::AddNodeDescendantsToView(TreeNode^ targetNode, int index, int offset)
|
||||
{
|
||||
if (targetNode->IsExpanded)
|
||||
{
|
||||
TreeNode^ childNode;
|
||||
for (int i = 0; i < (int)targetNode->Size; i++)
|
||||
{
|
||||
childNode = (TreeNode^)targetNode->GetAt(i);
|
||||
offset++;
|
||||
AddNodeToView(childNode, index + offset);
|
||||
offset = AddNodeDescendantsToView(childNode, index, offset);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void ViewModel::RemoveNodeAndDescendantsFromView(TreeNode^ targetNode)
|
||||
{
|
||||
if (targetNode->IsExpanded)
|
||||
{
|
||||
TreeNode^ childNode;
|
||||
for (int i = 0; i < (int)targetNode->Size; i++)
|
||||
{
|
||||
childNode = (TreeNode^)targetNode->GetAt(i);
|
||||
RemoveNodeAndDescendantsFromView(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
int index = IndexOf(targetNode);
|
||||
RemoveAt(index);
|
||||
}
|
||||
|
||||
int ViewModel::CountDescendants(TreeNode^ targetNode)
|
||||
{
|
||||
int descendantCount = 0;
|
||||
TreeNode^ childNode;
|
||||
for (int i = 0; i < (int)targetNode->Size; i++)
|
||||
{
|
||||
childNode = (TreeNode^)targetNode->GetAt(i);
|
||||
descendantCount++;
|
||||
if (childNode->IsExpanded)
|
||||
{
|
||||
descendantCount = descendantCount + CountDescendants(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
return descendantCount;
|
||||
}
|
||||
|
||||
int ViewModel::IndexOf(TreeNode^ targetNode)
|
||||
{
|
||||
unsigned int index;
|
||||
bool isIndexed = IndexOf(targetNode, &index);
|
||||
if (isIndexed)
|
||||
{
|
||||
return (int)index;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewModel::UpdateTreeView(IObservableVector<TreeNode^>^ sender, IVectorChangedEventArgs^ e)
|
||||
{
|
||||
VectorChanged(this, e);
|
||||
}
|
||||
|
||||
void ViewModel::TreeNodeVectorChanged(IBindableObservableVector^ sender, Platform::Object^ e)
|
||||
{
|
||||
CollectionChange CC = ((IVectorChangedEventArgs^)e)->CollectionChange;
|
||||
switch (CC)
|
||||
{
|
||||
//Reset case, commonly seen when a TreeNode is cleared.
|
||||
//removes all nodes that need removing then
|
||||
//toggles a collapse / expand to ensure order.
|
||||
case (CollectionChange::Reset) :
|
||||
{
|
||||
TreeNode^ resetNode = (TreeNode^)sender;
|
||||
int resetIndex = IndexOf(resetNode);
|
||||
if (resetIndex != Size - 1 && resetNode->IsExpanded)
|
||||
{
|
||||
TreeNode^ childNode = resetNode;
|
||||
TreeNode^ parentNode = resetNode->ParentNode;
|
||||
int stopIndex;
|
||||
bool isLastRelativeChild = true;
|
||||
while (parentNode != nullptr && isLastRelativeChild)
|
||||
{
|
||||
unsigned int relativeIndex;
|
||||
parentNode->IndexOf(childNode, &relativeIndex);
|
||||
if (parentNode->Size - 1 != relativeIndex)
|
||||
{
|
||||
isLastRelativeChild = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
childNode = parentNode;
|
||||
parentNode = parentNode->ParentNode;
|
||||
}
|
||||
}
|
||||
|
||||
if (parentNode != nullptr)
|
||||
{
|
||||
unsigned int siblingIndex;
|
||||
parentNode->IndexOf(childNode, &siblingIndex);
|
||||
TreeNode^ siblingNode = (TreeNode^)parentNode->GetAt(siblingIndex + 1);
|
||||
stopIndex = IndexOf(siblingNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
stopIndex = Size;
|
||||
}
|
||||
|
||||
for (int i = stopIndex - 1; i > resetIndex; i--)
|
||||
{
|
||||
if ((flatVectorRealizedItems->GetAt(i))->ParentNode == nullptr)
|
||||
{
|
||||
RemoveNodeAndDescendantsFromView(flatVectorRealizedItems->GetAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (resetNode->IsExpanded)
|
||||
{
|
||||
CollapseNode(resetNode);
|
||||
ExpandNode(resetNode);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Inserts the TreeNode into the correct index of the ViewModel
|
||||
case (CollectionChange::ItemInserted) :
|
||||
{
|
||||
//We will find the correct index of insertion by first checking if the
|
||||
//node we are inserting into is expanded. If it is we will start walking
|
||||
//down the tree and counting the open items. This is to ensure we place
|
||||
//the inserted item in the correct index. If along the way we bump into
|
||||
//the item being inserted, we insert there then return, because we don't
|
||||
//need to do anything further.
|
||||
unsigned int index = ((IVectorChangedEventArgs^)e)->Index;
|
||||
TreeNode^ targetNode = (TreeNode^)sender->GetAt(index);
|
||||
TreeNode^ parentNode = targetNode->ParentNode;
|
||||
TreeNode^ childNode;
|
||||
int parentIndex = IndexOf(parentNode);
|
||||
int allOpenedDescendantsCount = 0;
|
||||
if (parentNode->IsExpanded)
|
||||
{
|
||||
for (int i = 0; i < (int)parentNode->Size; i++)
|
||||
{
|
||||
childNode = (TreeNode^)parentNode->GetAt(i);
|
||||
if (childNode == targetNode)
|
||||
{
|
||||
AddNodeToView(targetNode, (parentIndex + i + 1 + allOpenedDescendantsCount));
|
||||
if (targetNode->IsExpanded)
|
||||
{
|
||||
AddNodeDescendantsToView(targetNode, parentIndex + i + 1, allOpenedDescendantsCount);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (childNode->IsExpanded)
|
||||
{
|
||||
allOpenedDescendantsCount += CountDescendants(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
AddNodeToView(targetNode, (parentIndex + parentNode->Size + allOpenedDescendantsCount));
|
||||
if (targetNode->IsExpanded)
|
||||
{
|
||||
AddNodeDescendantsToView(targetNode, parentIndex + parentNode->Size, allOpenedDescendantsCount);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Removes a node from the ViewModel when a TreeNode
|
||||
//removes a child.
|
||||
case (CollectionChange::ItemRemoved) :
|
||||
{
|
||||
TreeNode^ removeNode = (TreeNode^)sender;
|
||||
int removeIndex = IndexOf(removeNode);
|
||||
|
||||
if (removeIndex != Size - 1 && removeNode->IsExpanded)
|
||||
{
|
||||
TreeNode^ childNode = removeNode;
|
||||
TreeNode^ parentNode = removeNode->ParentNode;
|
||||
int stopIndex;
|
||||
bool isLastRelativeChild = true;
|
||||
while (parentNode != nullptr && isLastRelativeChild)
|
||||
{
|
||||
unsigned int relativeIndex;
|
||||
parentNode->IndexOf(childNode, &relativeIndex);
|
||||
if (parentNode->Size - 1 != relativeIndex)
|
||||
{
|
||||
isLastRelativeChild = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
childNode = parentNode;
|
||||
parentNode = parentNode->ParentNode;
|
||||
}
|
||||
}
|
||||
|
||||
if (parentNode != nullptr)
|
||||
{
|
||||
unsigned int siblingIndex;
|
||||
parentNode->IndexOf(childNode, &siblingIndex);
|
||||
TreeNode^ siblingNode = (TreeNode^)parentNode->GetAt(siblingIndex + 1);
|
||||
stopIndex = IndexOf(siblingNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
stopIndex = Size;
|
||||
}
|
||||
|
||||
for (int i = stopIndex - 1; i > removeIndex; i--)
|
||||
{
|
||||
if ((flatVectorRealizedItems->GetAt(i))->ParentNode == nullptr)
|
||||
{
|
||||
RemoveNodeAndDescendantsFromView(flatVectorRealizedItems->GetAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Triggered by a replace such as SetAt.
|
||||
//Updates the TreeNode that changed in the ViewModel.
|
||||
case (CollectionChange::ItemChanged) :
|
||||
{
|
||||
unsigned int index = ((IVectorChangedEventArgs^)e)->Index;
|
||||
TreeNode^ targetNode = (TreeNode^)sender->GetAt(index);
|
||||
TreeNode^ parentNode = targetNode->ParentNode;
|
||||
TreeNode^ childNode;
|
||||
int allOpenedDescendantsCount = 0;
|
||||
int parentIndex = IndexOf(parentNode);
|
||||
|
||||
for (int i = 0; i < (int)parentNode->Size; i++)
|
||||
{
|
||||
childNode = (TreeNode^)parentNode->GetAt(i);
|
||||
if (childNode->IsExpanded)
|
||||
{
|
||||
allOpenedDescendantsCount += CountDescendants(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
TreeNode^ removeNode = (TreeNode^)GetAt(parentIndex + index + allOpenedDescendantsCount + 1);
|
||||
if (removeNode->IsExpanded)
|
||||
{
|
||||
CollapseNode(removeNode);
|
||||
}
|
||||
|
||||
RemoveAt(parentIndex + index + allOpenedDescendantsCount + 1);
|
||||
InsertAt(parentIndex + index + allOpenedDescendantsCount + 1, targetNode);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ViewModel::TreeNodePropertyChanged(Object^ sender, PropertyChangedEventArgs^ e)
|
||||
{
|
||||
if (e->PropertyName == "IsExpanded")
|
||||
{
|
||||
TreeNode^ targetNode = (TreeNode^)sender;
|
||||
if (targetNode->IsExpanded)
|
||||
{
|
||||
if (targetNode->Size != 0)
|
||||
{
|
||||
int openedDescendantOffset = 0;
|
||||
int index = IndexOf(targetNode);
|
||||
index = index + 1;
|
||||
TreeNode^ childNode;
|
||||
for (int i = 0; i < (int)targetNode->Size; i++)
|
||||
{
|
||||
childNode = (TreeNode^)targetNode->GetAt(i);
|
||||
AddNodeToView(childNode, ((int)index + i + openedDescendantOffset));
|
||||
openedDescendantOffset = AddNodeDescendantsToView(childNode, (index + i), openedDescendantOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TreeNode^ childNode;
|
||||
for (int i = 0; i < (int)targetNode->Size; i++)
|
||||
{
|
||||
childNode = (TreeNode^)targetNode->GetAt(i);
|
||||
RemoveNodeAndDescendantsFromView(childNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
#include "TreeNode.h"
|
||||
|
||||
namespace TreeViewControl {
|
||||
/// <summary>
|
||||
/// The ViewModel is responsible for flattening the heirarchical data into a flat list.
|
||||
/// It also tracks changes to the underlying data and updates the flat list accordingly.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
ref class ViewModel sealed : Windows::UI::Xaml::Interop::IBindableObservableVector
|
||||
{
|
||||
internal:
|
||||
ViewModel();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Add root to the view model. In other cases the app should not
|
||||
/// use this api.
|
||||
/// </summary>
|
||||
virtual void Append(Object^ value);
|
||||
|
||||
virtual void Clear();
|
||||
|
||||
virtual Windows::UI::Xaml::Interop::IBindableIterator^ First();
|
||||
|
||||
virtual Object^ GetAt(unsigned int index);
|
||||
|
||||
virtual Windows::UI::Xaml::Interop::IBindableVectorView^ GetView();
|
||||
|
||||
virtual bool IndexOf(Object^ value, unsigned int* index);
|
||||
|
||||
virtual void InsertAt(unsigned int index, Object^ value);
|
||||
|
||||
virtual void RemoveAt(unsigned int index);
|
||||
|
||||
virtual void RemoveAtEnd();
|
||||
|
||||
virtual void SetAt(unsigned int index, Object^ value);
|
||||
|
||||
virtual property unsigned int Size
|
||||
{
|
||||
unsigned int get() { return flatVectorRealizedItems->Size; };
|
||||
};
|
||||
|
||||
virtual event Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ VectorChanged
|
||||
{
|
||||
virtual Windows::Foundation::EventRegistrationToken add(Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ args)
|
||||
{
|
||||
return ViewModelChanged += args;
|
||||
}
|
||||
|
||||
virtual void remove(Windows::Foundation::EventRegistrationToken token)
|
||||
{
|
||||
return ViewModelChanged -= token;
|
||||
}
|
||||
|
||||
virtual void raise(Windows::UI::Xaml::Interop::IBindableObservableVector^ vector, Platform::Object^ e)
|
||||
{
|
||||
ViewModelChanged(vector, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ExpandNode adds the children and all open descendants of the targetNode
|
||||
/// to the ViewModel in their correct flattened index.
|
||||
/// </summary>
|
||||
void ExpandNode(TreeNode^ targetNode);
|
||||
|
||||
/// <summary>
|
||||
/// Collapse node removes all the descendants of the targetNode from the ViewModel.
|
||||
/// </summary>
|
||||
void CollapseNode(TreeNode^ targetNode);
|
||||
|
||||
/// <summary>
|
||||
/// This is the collection changed handler for individual TreeNodes. The collection changes
|
||||
/// from the hierarchical TreeNodes need to be flattened so that we can keep our current
|
||||
/// flat list in sync.
|
||||
/// </summary>
|
||||
/// <param name="sender">TreeNode that has already been changed</param>
|
||||
void TreeNodeVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector^ sender, Platform::Object^ e);
|
||||
|
||||
void TreeNodePropertyChanged(Object^ sender, Windows::UI::Xaml::Data::PropertyChangedEventArgs^ e);
|
||||
|
||||
private:
|
||||
event Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler^ ViewModelChanged;
|
||||
|
||||
Platform::Collections::Vector<TreeNode^>^ flatVectorRealizedItems = ref new Platform::Collections::Vector<TreeNode^>();
|
||||
|
||||
std::vector<Windows::Foundation::EventRegistrationToken> collectionChangedEventTokenVector;
|
||||
|
||||
std::vector<Windows::Foundation::EventRegistrationToken> propertyChangedEventTokenVector;
|
||||
|
||||
void AddNodeToView(TreeNode^ targetNode, int index);
|
||||
|
||||
int AddNodeDescendantsToView(TreeNode^ targetNode, int start, int offset);
|
||||
|
||||
void RemoveNodeAndDescendantsFromView(TreeNode^ targetNode);
|
||||
|
||||
int CountDescendants(TreeNode^ targetNode);
|
||||
|
||||
int IndexOf(TreeNode^ targetNode);
|
||||
|
||||
void UpdateTreeView(Windows::Foundation::Collections::IObservableVector<TreeNode^>^ sender, Windows::Foundation::Collections::IVectorChangedEventArgs^ e);
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
#include "pch.h"
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <collection.h>
|
||||
#include <ppltasks.h>
|
||||
#include <TreeNode.h>
|
||||
#include <ViewModel.h>
|
||||
#include <TreeViewItem.h>
|
||||
#include <TreeView.h>
|
||||
#include <TreeViewItemAutomationPeer.h>
|
||||
#include <IntegerToIndentationConverter.h>
|
|
@ -0,0 +1,166 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{F6FAFE3C-92C0-43E7-A64B-B38DFC793EB2}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>GLBGLTFConverter</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="GLBGLTFConverter.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\gltfparser\gltfparser.vcxproj">
|
||||
<Project>{d3b2132d-132e-4b58-a828-b20ef9962a83}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GLBGLTFConverter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,8 @@
|
|||
<Application
|
||||
x:Class="ModelViewer.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:ModelViewer"
|
||||
RequestedTheme="Dark">
|
||||
|
||||
</Application>
|
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// App.xaml.cpp
|
||||
// Implementation of the App class.
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
#include "RootPage.xaml.h"
|
||||
#include "DirectXPage.xaml.h"
|
||||
|
||||
using namespace ModelViewer;
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::ApplicationModel;
|
||||
using namespace Windows::ApplicationModel::Activation;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
using namespace Windows::UI::Xaml::Input;
|
||||
using namespace Windows::UI::Xaml::Interop;
|
||||
using namespace Windows::UI::Xaml::Media;
|
||||
using namespace Windows::UI::Xaml::Navigation;
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
App::App()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
//builder.registerType<>
|
||||
|
||||
Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);
|
||||
Resuming += ref new EventHandler<Object^>(this, &App::OnResuming);
|
||||
}
|
||||
|
||||
void App::ExtendAcrylicIntoTitleBar()
|
||||
{
|
||||
Windows::ApplicationModel::Core::CoreApplication::GetCurrentView()->TitleBar->ExtendViewIntoTitleBar = true;
|
||||
auto titleBar = Windows::ApplicationModel::Core::CoreApplication::GetCurrentView()->TitleBar;
|
||||
// These properties don't exist...
|
||||
//titleBar->ButtonBackgroundColor = Colors.Transparent;
|
||||
//titleBar->ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched normally by the end user. Other entry points
|
||||
/// will be used when the application is launched to open a specific file, to display
|
||||
/// search results, and so forth.
|
||||
/// </summary>
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e)
|
||||
{
|
||||
#if _DEBUG
|
||||
if (IsDebuggerPresent())
|
||||
{
|
||||
DebugSettings->EnableFrameRateCounter = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_rootPage == nullptr)
|
||||
{
|
||||
m_rootPage = ref new ModelViewer::RootPage();
|
||||
}
|
||||
|
||||
if (e->PreviousExecutionState == ApplicationExecutionState::Terminated)
|
||||
{
|
||||
m_rootPage->LoadInternalState(ApplicationData::Current->LocalSettings->Values);
|
||||
}
|
||||
|
||||
auto rootFrame = dynamic_cast<Frame^>(Window::Current->Content);
|
||||
|
||||
// Do not repeat app initialization when the Window already has content,
|
||||
// just ensure that the window is active
|
||||
if (rootFrame == nullptr)
|
||||
{
|
||||
// Create a Frame to act as the navigation context and associate it with
|
||||
// a SuspensionManager key
|
||||
rootFrame = ref new Frame();
|
||||
|
||||
rootFrame->NavigationFailed += ref new Windows::UI::Xaml::Navigation::NavigationFailedEventHandler(this, &App::OnNavigationFailed);
|
||||
|
||||
if (rootFrame->Content == nullptr)
|
||||
{
|
||||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
rootFrame->Navigate(TypeName(RootPage::typeid), e->Arguments);
|
||||
}
|
||||
|
||||
// Place the frame in the current Window
|
||||
Window::Current->Content = rootFrame;
|
||||
// Ensure the current window is active
|
||||
Window::Current->Activate();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rootFrame->Content == nullptr)
|
||||
{
|
||||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
rootFrame->Navigate(TypeName(RootPage::typeid), e->Arguments);
|
||||
}
|
||||
|
||||
|
||||
// Ensure the current window is active
|
||||
Window::Current->Activate();
|
||||
ExtendAcrylicIntoTitleBar();
|
||||
}
|
||||
|
||||
//m_rootPage->GetContentFrame()->Content = ref new DirectXPage();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when application execution is being suspended. Application state is saved
|
||||
/// without knowing whether the application will be terminated or resumed with the contents
|
||||
/// of memory still intact.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the suspend request.</param>
|
||||
/// <param name="e">Details about the suspend request.</param>
|
||||
void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e)
|
||||
{
|
||||
(void) sender; // Unused parameter
|
||||
(void) e; // Unused parameter
|
||||
|
||||
m_rootPage->SaveInternalState(ApplicationData::Current->LocalSettings->Values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when application execution is being resumed.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the resume request.</param>
|
||||
/// <param name="args">Details about the resume request.</param>
|
||||
void App::OnResuming(Object ^sender, Object ^args)
|
||||
{
|
||||
(void) sender; // Unused parameter
|
||||
(void) args; // Unused parameter
|
||||
|
||||
m_rootPage->LoadInternalState(ApplicationData::Current->LocalSettings->Values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when Navigation to a certain page fails
|
||||
/// </summary>
|
||||
/// <param name="sender">The Frame which failed navigation</param>
|
||||
/// <param name="e">Details about the navigation failure</param>
|
||||
void App::OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e)
|
||||
{
|
||||
throw ref new FailureException("Failed to load Page " + e->SourcePageType.Name);
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// App.xaml.h
|
||||
// Declaration of the App class.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "App.g.h"
|
||||
#include "RootPage.xaml.h"
|
||||
|
||||
namespace ModelViewer
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
ref class App sealed
|
||||
{
|
||||
public:
|
||||
App();
|
||||
virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) override;
|
||||
|
||||
private:
|
||||
void ExtendAcrylicIntoTitleBar();
|
||||
|
||||
void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ e);
|
||||
void OnResuming(Platform::Object ^sender, Platform::Object ^args);
|
||||
void OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e);
|
||||
RootPage^ m_rootPage;
|
||||
};
|
||||
}
|
После Ширина: | Высота: | Размер: 1.4 KiB |
|
@ -0,0 +1,359 @@
|
|||
//
|
||||
// This fragment shader defines a reference implementation for Physically Based Shading of
|
||||
// a microfacet surface material defined by a glTF model.
|
||||
//
|
||||
// References:
|
||||
// [1] Real Shading in Unreal Engine 4
|
||||
// http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
||||
// [2] Physically Based Shading at Disney
|
||||
// http://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf
|
||||
// [3] README.md - Environment Maps
|
||||
// https://github.com/KhronosGroup/glTF-WebGL-PBR/#environment-maps
|
||||
// [4] "An Inexpensive BRDF Model for Physically based Rendering" by Christophe Schlick
|
||||
// https://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf
|
||||
|
||||
#define NORMALS
|
||||
#define UV
|
||||
#define HAS_NORMALS
|
||||
#define USE_IBL
|
||||
#define USE_TEX_LOD
|
||||
|
||||
Texture2D baseColourTexture : register(t0);
|
||||
SamplerState baseColourSampler : register(s0);
|
||||
|
||||
Texture2D normalTexture : register(t1);
|
||||
SamplerState normalSampler : register(s1);
|
||||
|
||||
Texture2D emissionTexture : register(t2);
|
||||
SamplerState emissionSampler : register(s2);
|
||||
|
||||
Texture2D occlusionTexture : register(t3);
|
||||
SamplerState occlusionSampler : register(s3);
|
||||
|
||||
Texture2D metallicRoughnessTexture : register(t4);
|
||||
SamplerState metallicRoughnessSampler : register(s4);
|
||||
|
||||
TextureCube envDiffuseTexture : register(t8);
|
||||
SamplerState envDiffuseSampler : register(s8);
|
||||
|
||||
Texture2D brdfLutTexture : register(t9);
|
||||
SamplerState brdfLutSampler : register(s9);
|
||||
|
||||
TextureCube envSpecularTexture : register(t10);
|
||||
SamplerState envSpecularSampler : register(s10);
|
||||
|
||||
struct Light
|
||||
{
|
||||
float3 dir;
|
||||
float padding1;
|
||||
float3 colour;
|
||||
float padding2;
|
||||
};
|
||||
|
||||
cbuffer cbPerFrame : register(b0)
|
||||
{
|
||||
Light light;
|
||||
};
|
||||
|
||||
cbuffer cbPerObject : register(b1)
|
||||
{
|
||||
float normalScale;
|
||||
float3 emissiveFactor;
|
||||
float occlusionStrength;
|
||||
float2 metallicRoughnessValues;
|
||||
float padding1;
|
||||
float4 baseColorFactor;
|
||||
float3 camera;
|
||||
float padding2;
|
||||
|
||||
// debugging flags used for shader output of intermediate PBR variables
|
||||
float4 scaleDiffBaseMR;
|
||||
float4 scaleFGDSpec;
|
||||
float4 scaleIBLAmbient;
|
||||
};
|
||||
|
||||
// Per-pixel color data passed through the pixel shader.
|
||||
struct PixelShaderInput
|
||||
{
|
||||
float4 position : SV_POSITION;
|
||||
float3 poswithoutw : POSITION;
|
||||
|
||||
#ifdef NORMALS
|
||||
float3 normal : NORMAL;
|
||||
#endif
|
||||
#ifdef UV
|
||||
float2 texcoord : TEXCOORD0;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef HAS_NORMALS
|
||||
#ifdef HAS_TANGENTS
|
||||
varying mat3 v_TBN;
|
||||
#else
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Encapsulate the various inputs used by the various functions in the shading equation
|
||||
// We store values in this struct to simplify the integration of alternative implementations
|
||||
// of the shading terms, outlined in the Readme.MD Appendix.
|
||||
struct PBRInfo
|
||||
{
|
||||
float NdotL; // cos angle between normal and light direction
|
||||
float NdotV; // cos angle between normal and view direction
|
||||
float NdotH; // cos angle between normal and half vector
|
||||
float LdotH; // cos angle between light direction and half vector
|
||||
float VdotH; // cos angle between view direction and half vector
|
||||
float perceptualRoughness; // roughness value, as authored by the model creator (input to shader)
|
||||
float metalness; // metallic value at the surface
|
||||
float3 reflectance0; // full reflectance color (normal incidence angle)
|
||||
float3 reflectance90; // reflectance color at grazing angle
|
||||
float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2])
|
||||
float3 diffuseColor; // color contribution from diffuse lighting
|
||||
float3 specularColor; // color contribution from specular lighting
|
||||
};
|
||||
|
||||
static const float M_PI = 3.141592653589793;
|
||||
static const float c_MinRoughness = 0.04;
|
||||
|
||||
float4 SRGBtoLINEAR(float4 srgbIn)
|
||||
{
|
||||
#ifdef MANUAL_SRGB
|
||||
#ifdef SRGB_FAST_APPROXIMATION
|
||||
float3 linOut = pow(srgbIn.xyz,float3(2.2, 2.2, 2.2));
|
||||
#else //SRGB_FAST_APPROXIMATION
|
||||
float3 bLess = step(float3(0.04045, 0.04045, 0.04045), srgbIn.xyz);
|
||||
float3 linOut = lerp(srgbIn.xyz / float3(12.92, 12.92, 12.92), pow((srgbIn.xyz + float3(0.055, 0.055, 0.055)) / float3(1.055, 1.055, 1.055), float3(2.4, 2.4, 2.4)), bLess);
|
||||
#endif //SRGB_FAST_APPROXIMATION
|
||||
return float4(linOut,srgbIn.w);;
|
||||
#else //MANUAL_SRGB
|
||||
return srgbIn;
|
||||
#endif //MANUAL_SRGB
|
||||
}
|
||||
|
||||
// Find the normal for this fragment, pulling either from a predefined normal map
|
||||
// or from the interpolated mesh normal and tangent attributes.
|
||||
float3 getNormal(float3 position, float3 normal, float2 uv)
|
||||
{
|
||||
// Retrieve the tangent space matrix
|
||||
#ifndef HAS_TANGENTS
|
||||
float3 pos_dx = ddx(position);
|
||||
float3 pos_dy = ddy(position);
|
||||
float3 tex_dx = ddx(float3(uv, 0.0));
|
||||
float3 tex_dy = ddy(float3(uv, 0.0));
|
||||
float3 t = (tex_dy.y * pos_dx - tex_dx.y * pos_dy) / (tex_dx.x * tex_dy.y - tex_dy.x * tex_dx.y);
|
||||
|
||||
#ifdef HAS_NORMALS
|
||||
float3 ng = normalize(normal);
|
||||
#else
|
||||
float3 ng = cross(pos_dx, pos_dy);
|
||||
#endif
|
||||
|
||||
t = normalize(t - ng * dot(ng, t));
|
||||
float3 b = normalize(cross(ng, t));
|
||||
row_major float3x3 tbn = float3x3(t, b, ng);
|
||||
|
||||
#else // HAS_TANGENTS
|
||||
mat3 tbn = v_TBN;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_NORMALMAP
|
||||
float3 n = normalTexture.Sample(normalSampler, uv).rgb;
|
||||
|
||||
// Need to check the multiplication is equivalent..
|
||||
n = normalize(mul(((2.0 * n - 1.0) * float3(normalScale, normalScale, 1.0)), tbn));
|
||||
#else
|
||||
float3 n = tbn[2].xyz;
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#ifdef USE_IBL
|
||||
// Calculation of the lighting contribution from an optional Image Based Light source.
|
||||
// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
|
||||
// See our README.md on Environment Maps [3] for additional discussion.
|
||||
float3 getIBLContribution(PBRInfo pbrInputs, float3 n, float3 reflection)
|
||||
{
|
||||
float mipCount = 9.0; // resolution of 512x512
|
||||
float lod = (pbrInputs.perceptualRoughness * mipCount);
|
||||
|
||||
// retrieve a scale and bias to F0. See [1], Figure 3
|
||||
float2 val = float2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness);
|
||||
float3 brdf = SRGBtoLINEAR(brdfLutTexture.Sample(brdfLutSampler, val)).rgb;
|
||||
|
||||
float3 diffuseLight = SRGBtoLINEAR(envDiffuseTexture.Sample(envDiffuseSampler, n)).rgb;
|
||||
|
||||
#ifdef USE_TEX_LOD
|
||||
float3 specularLight = SRGBtoLINEAR(envSpecularTexture.SampleLevel(envSpecularSampler, reflection, 0)).rgb;
|
||||
#else
|
||||
float3 specularLight = SRGBtoLINEAR(envSpecularTexture.Sample(envSpecularSampler, reflection)).rgb;
|
||||
#endif
|
||||
|
||||
float3 diffuse = diffuseLight * pbrInputs.diffuseColor;
|
||||
float3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);
|
||||
|
||||
// For presentation, this allows us to disable IBL terms
|
||||
diffuse *= scaleIBLAmbient.x;
|
||||
specular *= scaleIBLAmbient.y;
|
||||
|
||||
return diffuse + specular;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Basic Lambertian diffuse
|
||||
// Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog
|
||||
// See also [1], Equation 1
|
||||
float3 diffuse(PBRInfo pbrInputs)
|
||||
{
|
||||
return pbrInputs.diffuseColor / M_PI;
|
||||
}
|
||||
|
||||
// The following equation models the Fresnel reflectance term of the spec equation (aka F())
|
||||
// Implementation of fresnel from [4], Equation 15
|
||||
float3 specularReflection(PBRInfo pbrInputs)
|
||||
{
|
||||
return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
// This calculates the specular geometric attenuation (aka G()),
|
||||
// where rougher material will reflect less light back to the viewer.
|
||||
// This implementation is based on [1] Equation 4, and we adopt their modifications to
|
||||
// alphaRoughness as input as originally proposed in [2].
|
||||
float geometricOcclusion(PBRInfo pbrInputs)
|
||||
{
|
||||
float NdotL = pbrInputs.NdotL;
|
||||
float NdotV = pbrInputs.NdotV;
|
||||
float r = pbrInputs.alphaRoughness;
|
||||
|
||||
float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
|
||||
float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
|
||||
return attenuationL * attenuationV;
|
||||
}
|
||||
|
||||
// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
|
||||
// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
|
||||
// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3.
|
||||
float microfacetDistribution(PBRInfo pbrInputs)
|
||||
{
|
||||
float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
|
||||
float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
|
||||
return roughnessSq / (M_PI * f * f);
|
||||
}
|
||||
|
||||
float4 main(PixelShaderInput input) : SV_TARGET
|
||||
{
|
||||
// Metallic and Roughness material properties are packed together
|
||||
// In glTF, these factors can be specified by fixed scalar values
|
||||
// or from a metallic-roughness map
|
||||
float perceptualRoughness = metallicRoughnessValues.y;
|
||||
float metallic = metallicRoughnessValues.x;
|
||||
|
||||
#ifdef HAS_METALROUGHNESSMAP
|
||||
// Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
|
||||
// This layout intentionally reserves the 'r' channel for (optional) occlusion map data
|
||||
float4 mrSample = metallicRoughnessTexture.Sample(metallicRoughnessSampler, input.texcoord);
|
||||
|
||||
// Had to reverse the order of the channels here - TODO: investigate..
|
||||
perceptualRoughness = mrSample.g * perceptualRoughness;
|
||||
metallic = mrSample.b * metallic;
|
||||
#endif
|
||||
|
||||
perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
|
||||
metallic = clamp(metallic, 0.0, 1.0);
|
||||
|
||||
// Roughness is authored as perceptual roughness; as is convention,
|
||||
// convert to material roughness by squaring the perceptual roughness [2].
|
||||
float alphaRoughness = perceptualRoughness * perceptualRoughness;
|
||||
|
||||
// The albedo may be defined from a base texture or a flat color
|
||||
|
||||
#ifdef HAS_BASECOLORMAP
|
||||
float4 baseColor = SRGBtoLINEAR(baseColourTexture.Sample(baseColourSampler, input.texcoord)) * baseColorFactor;
|
||||
#else
|
||||
float4 baseColor = baseColorFactor;
|
||||
#endif
|
||||
|
||||
float3 f0 = float3(0.04, 0.04, 0.04);
|
||||
float3 diffuseColor = baseColor.rgb * (float3(1.0, 1.0, 1.0) - f0);
|
||||
|
||||
diffuseColor *= 1.0 - metallic;
|
||||
|
||||
float3 specularColor = lerp(f0, baseColor.rgb, metallic);
|
||||
|
||||
// Compute reflectance.
|
||||
float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
|
||||
|
||||
// For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
|
||||
// For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
|
||||
float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
|
||||
float3 specularEnvironmentR0 = specularColor.rgb;
|
||||
float3 specularEnvironmentR90 = float3(1.0, 1.0, 1.0) * reflectance90;
|
||||
|
||||
float3 n = getNormal(input.poswithoutw, input.normal, input.texcoord); // normal at surface point
|
||||
float3 v = normalize(camera - input.poswithoutw); // Vector from surface point to camera
|
||||
|
||||
float3 l = normalize(light.dir); // Vector from surface point to light
|
||||
float3 h = normalize(l + v); // Half vector between both l and v
|
||||
float3 reflection = -normalize(reflect(v, n));
|
||||
|
||||
float NdotL = clamp(dot(n, l), 0.001, 1.0);
|
||||
float NdotV = abs(dot(n, v)) + 0.001;
|
||||
float NdotH = clamp(dot(n, h), 0.0, 1.0);
|
||||
float LdotH = clamp(dot(l, h), 0.0, 1.0);
|
||||
float VdotH = clamp(dot(v, h), 0.0, 1.0);
|
||||
|
||||
PBRInfo pbrInputs;
|
||||
pbrInputs.NdotL = NdotL;
|
||||
pbrInputs.NdotV = NdotV;
|
||||
pbrInputs.NdotH = NdotH;
|
||||
pbrInputs.LdotH = LdotH;
|
||||
pbrInputs.VdotH = VdotH;
|
||||
pbrInputs.perceptualRoughness = perceptualRoughness;
|
||||
pbrInputs.metalness = metallic;
|
||||
pbrInputs.reflectance0 = specularEnvironmentR0;
|
||||
pbrInputs.reflectance90 = specularEnvironmentR90;
|
||||
pbrInputs.alphaRoughness = alphaRoughness;
|
||||
pbrInputs.diffuseColor = diffuseColor;
|
||||
pbrInputs.specularColor = specularColor;
|
||||
|
||||
// Calculate the shading terms for the microfacet specular shading model
|
||||
float3 F = specularReflection(pbrInputs);
|
||||
|
||||
float G = geometricOcclusion(pbrInputs);
|
||||
float D = microfacetDistribution(pbrInputs);
|
||||
|
||||
// Calculation of analytical lighting contribution
|
||||
float3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs);
|
||||
float3 specContrib = F * G * D / (4.0 * NdotL * NdotV);
|
||||
float3 color = NdotL * light.colour * (diffuseContrib + specContrib);
|
||||
|
||||
|
||||
// Calculate lighting contribution from image based lighting source (IBL)
|
||||
#ifdef USE_IBL
|
||||
color += getIBLContribution(pbrInputs, n, reflection);
|
||||
#endif
|
||||
|
||||
// Apply optional PBR terms for additional (optional) shading
|
||||
#ifdef HAS_OCCLUSIONMAP
|
||||
float ao = occlusionTexture.Sample(occlusionSampler, input.texcoord).r;
|
||||
color = lerp(color, color * ao, occlusionStrength);
|
||||
#endif
|
||||
|
||||
#ifdef HAS_EMISSIVEMAP
|
||||
float3 emissive = SRGBtoLINEAR(emissionTexture.Sample(emissionSampler, input.texcoord)).rgb * emissiveFactor;
|
||||
color += emissive;
|
||||
#endif
|
||||
|
||||
// This section uses lerp to override final color for reference app visualization
|
||||
// of various parameters in the lighting equation.
|
||||
color = lerp(color, F, scaleFGDSpec.x);
|
||||
color = lerp(color, float3(G, G, G), scaleFGDSpec.y);
|
||||
color = lerp(color, float3(D, D, D), scaleFGDSpec.z);
|
||||
color = lerp(color, specContrib, scaleFGDSpec.w);
|
||||
color = lerp(color, diffuseContrib, scaleDiffBaseMR.x);
|
||||
color = lerp(color, baseColor.rgb, scaleDiffBaseMR.y);
|
||||
color = lerp(color, float3(metallic, metallic, metallic), scaleDiffBaseMR.z);
|
||||
color = lerp(color, float3(perceptualRoughness, perceptualRoughness, perceptualRoughness), scaleDiffBaseMR.w);
|
||||
|
||||
return float4(color, 1.0);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
//#define NORMALS
|
||||
//#define UV
|
||||
|
||||
// A constant buffer that stores the three basic column-major matrices for composing geometry.
|
||||
cbuffer ModelViewProjectionConstantBuffer : register(b0)
|
||||
{
|
||||
matrix model;
|
||||
matrix view;
|
||||
matrix projection;
|
||||
};
|
||||
|
||||
// Per-vertex data used as input to the vertex shader.
|
||||
struct VertexShaderInput
|
||||
{
|
||||
float4 position : POSITION;
|
||||
#ifdef NORMALS
|
||||
float3 normal : NORMAL;
|
||||
#endif
|
||||
#ifdef UV
|
||||
float2 texcoord : TEXCOORD0;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Per-pixel color data passed through the pixel shader.
|
||||
struct PixelShaderInput
|
||||
{
|
||||
float4 position : SV_POSITION;
|
||||
float3 poswithoutw : POSITION;
|
||||
|
||||
#ifdef NORMALS
|
||||
float3 normal : NORMAL;
|
||||
#endif
|
||||
|
||||
float2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
PixelShaderInput main(VertexShaderInput input)
|
||||
{
|
||||
PixelShaderInput output;
|
||||
|
||||
// Transform the vertex position into projected space.
|
||||
float4 pos = mul(input.position, model);
|
||||
output.poswithoutw = float3(pos.xyz) / pos.w;
|
||||
|
||||
#ifdef NORMALS
|
||||
// If we have normals...
|
||||
output.normal = normalize(mul(float4(input.normal.xyz, 0.0), model));
|
||||
#endif
|
||||
|
||||
#ifdef UV
|
||||
output.texcoord = input.texcoord;
|
||||
#else
|
||||
output.texcoord = float2(0.0f, 0.0f);
|
||||
#endif
|
||||
|
||||
#ifdef HAS_NORMALS
|
||||
#ifdef HAS_TANGENTS
|
||||
vec3 normalW = normalize(vec3(u_ModelMatrix * vec4(a_Normal.xyz, 0.0)));
|
||||
vec3 tangentW = normalize(vec3(u_ModelMatrix * vec4(a_Tangent.xyz, 0.0)));
|
||||
vec3 bitangentW = cross(normalW, tangentW) * a_Tangent.w;
|
||||
v_TBN = mat3(tangentW, bitangentW, normalW);
|
||||
#else // HAS_TANGENTS != 1
|
||||
v_Normal = normalize(vec3(u_ModelMatrix * vec4(a_Normal.xyz, 0.0)));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Transform the vertex position into projected space.
|
||||
pos = mul(pos, view);
|
||||
pos = mul(pos, projection);
|
||||
output.position = pos;
|
||||
|
||||
return output;
|
||||
}
|
После Ширина: | Высота: | Размер: 7.5 KiB |
После Ширина: | Высота: | Размер: 2.9 KiB |
После Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 1.4 KiB |
После Ширина: | Высота: | Размер: 3.1 KiB |
После Ширина: | Высота: | Размер: 16 KiB |
После Ширина: | Высота: | Размер: 24 KiB |
После Ширина: | Высота: | Размер: 23 KiB |
После Ширина: | Высота: | Размер: 22 KiB |
После Ширина: | Высота: | Размер: 24 KiB |
После Ширина: | Высота: | Размер: 23 KiB |
После Ширина: | Высота: | Размер: 18 KiB |
Двоичные данные
ModelViewer/Assets/textures/papermill/environment/environment_back_0.jpg
Normal file
После Ширина: | Высота: | Размер: 140 KiB |
Двоичные данные
ModelViewer/Assets/textures/papermill/environment/environment_bottom_0.jpg
Normal file
После Ширина: | Высота: | Размер: 107 KiB |
Двоичные данные
ModelViewer/Assets/textures/papermill/environment/environment_front_0.jpg
Normal file
После Ширина: | Высота: | Размер: 148 KiB |
Двоичные данные
ModelViewer/Assets/textures/papermill/environment/environment_left_0.jpg
Normal file
После Ширина: | Высота: | Размер: 126 KiB |
Двоичные данные
ModelViewer/Assets/textures/papermill/environment/environment_right_0.jpg
Normal file
После Ширина: | Высота: | Размер: 137 KiB |
После Ширина: | Высота: | Размер: 145 KiB |
После Ширина: | Высота: | Размер: 173 KiB |
После Ширина: | Высота: | Размер: 45 KiB |
После Ширина: | Высота: | Размер: 29 KiB |
После Ширина: | Высота: | Размер: 25 KiB |
После Ширина: | Высота: | Размер: 24 KiB |
После Ширина: | Высота: | Размер: 23 KiB |
После Ширина: | Высота: | Размер: 23 KiB |
После Ширина: | Высота: | Размер: 23 KiB |
После Ширина: | Высота: | Размер: 23 KiB |
После Ширина: | Высота: | Размер: 23 KiB |
После Ширина: | Высота: | Размер: 119 KiB |
После Ширина: | Высота: | Размер: 31 KiB |
После Ширина: | Высота: | Размер: 23 KiB |
После Ширина: | Высота: | Размер: 20 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 182 KiB |
После Ширина: | Высота: | Размер: 42 KiB |
После Ширина: | Высота: | Размер: 25 KiB |
После Ширина: | Высота: | Размер: 21 KiB |
После Ширина: | Высота: | Размер: 20 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 149 KiB |
После Ширина: | Высота: | Размер: 38 KiB |
После Ширина: | Высота: | Размер: 24 KiB |
После Ширина: | Высота: | Размер: 21 KiB |
После Ширина: | Высота: | Размер: 20 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 169 KiB |
После Ширина: | Высота: | Размер: 39 KiB |
После Ширина: | Высота: | Размер: 24 KiB |
После Ширина: | Высота: | Размер: 21 KiB |
После Ширина: | Высота: | Размер: 20 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 19 KiB |
После Ширина: | Высота: | Размер: 174 KiB |
После Ширина: | Высота: | Размер: 42 KiB |