项目结构调整

This commit is contained in:
艾竹
2023-04-16 20:11:40 +08:00
parent cbfbf96033
commit 81f91f3f35
2124 changed files with 218 additions and 5516 deletions

View File

@@ -0,0 +1,14 @@
; Top-most http://editorconfig.org/ file
root = true
[*]
end_of_line = CRLF
; 4-column tab indentation
[*.{cs,csproj,xaml,xml,props,targets}]
indent_style = space
indent_size = 4
[*.{md,yml}]
indent_style = space
indent_size = 2

32
Others/gong-wpf-dragdrop/.gitattributes vendored Normal file
View File

@@ -0,0 +1,32 @@
# Auto detect text files and perform LF normalization
* text=auto
# These files are text and should be normalized (convert crlf => lf)
# Custom for Visual Studio
*.cs diff=csharp
*.xaml merge=union
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
# Images should be treated as binary
# (binary is a macro for -text -diff)
*.png binary
*.jepg binary
*.jpg binary
*.sdf binary

View File

@@ -0,0 +1,113 @@
# Contributing to this project
Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved.
Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features.
## Using the issue tracker
The issue tracker is the preferred channel for [bug reports](#bugs), [features requests](#features) and [submitting pull requests](#pull-requests), but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests (use [Stack Overflow](http://stackoverflow.com) or [Gitter](https://gitter.im/punker76/gong-wpf-dragdrop)).
* Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others.
<a name="bugs"></a>
## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are extremely helpful - **thank you**!
Guidelines for bug reports:
1. **Use the GitHub issue search** &mdash; check if the issue has already been reported.
2. **Check if the issue has been fixed** &mdash; try to reproduce it using the latest `master` or development branch in the repository.
3. **Isolate the problem** &mdash; create an example or perhaps even a test.
A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report.
- What is [the version](#version) of the library which you are using.
- What is your environment?
- What steps will reproduce the issue?
- What OS experience the problem?
- What would you expect to be the outcome?
All these details will help people to fix any potential bugs.
<a name="repro"></a>
## A repro is most excellent
If you can include a working demo of the issue, you're 90% of the way there. This helps us greatly to see the problem and debug it much quicker. Without it, we need to understand how you are using it, which takes time.
<a name="version"></a>
## What version?
Please include the version of the library which you are using. This will help us to isolate the issue quicker.
## Details are crucial
Pictures help to explain visual issues. A video or screencast helps to see animation issues. If these are relevant to your use cases, please include them.
<a name="features"></a>
## Bugs vs Features
A bug is something that is wrong with a specific version of the application. A feature is a missing or new piece of functionality.
While we are happy to hear about requested features, new development is not a high-priority in the short-term.
<a name="pull-requests"></a>
## Pull requests
Good pull requests - patches, improvements, new features - are a fantastic help. They should remain focused in scope and avoid containing unrelated commits.
**Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code, porting to a different language), otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project.
Please adhere to the coding conventions used throughout a project (indentation, accurate comments, etc.) and any other requirements.
Follow this process if you'd like your work considered for inclusion in the project:
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes:
```bash
# Clone your fork of the repo into the current directory
git clone https://github.com/<your-username>/<repo-name>
# Navigate to the newly cloned directory
cd <repo-name>
# Assign the original repo to a remote called "upstream"
git remote add upstream https://github.com/<upstream-owner>/<repo-name>
```
2. If you cloned a while ago, get the latest changes from upstream:
```bash
git checkout <dev-branch>
git pull upstream <dev-branch>
```
3. Create a new topic branch (off the main project development branch) to contain your feature, change, or fix:
```bash
git checkout -b <topic-branch-name>
```
4. Commit your changes in logical chunks. Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
or your code is unlikely be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public.
5. Locally merge (or rebase) the upstream development branch into your topic branch:
```bash
git pull [--rebase] upstream <dev-branch>
```
6. Push your topic branch up to your fork:
```bash
git push origin <topic-branch-name>
```
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description.
**IMPORTANT**: By submitting a patch, you agree to allow the project owner to license your work under the same license as that used by the project.
Most text of this contributing is taken from this [issue-guidelines](https://github.com/necolas/issue-guidelines).

View File

@@ -0,0 +1,35 @@
# PLEASE READ BEFORE POSTING AN ISSUE
This is a place for issues for GongSolutions.WPF.DragDrop itself. Questions are better asked in the [Gitter](https://gitter.im/punker76/gong-wpf-dragdrop) room.
### Seriously consider creating and linking to a test repo which takes absolutely minimal setup to illustrate how reproduce the problem.
My time is limited and .Zip files, code snippets and partial examples are often a time sink. GitHub also provides great communication and code review tools which can be utilised.
## HOW TO POST A GOOD SAMPLE.
Follow these guidelines and I will most likely look at the issue sooner:
* Post a full GitHub repository. Not a zip file, half baked snippet etc. If GitHub is new to you consider it a great learning opportunity and chance to get involved.
* The repository should have just ONE step max for me to get running, and that is "Restore NuGet Packages". If there are any other missing dependencies, or uncompiling features I will most likely move on to another issue.
* I'm not trying to be awkward. I'm just busy and I'm helping a lot of people, not just you, so help me out and I will help you out.
## What steps will reproduce this issue?
_Write problem description here_
### Expected outcome
_Write expected outcome here_
### Repo
_Link to the Repo_
### Environment
- GongSolutions.WPF.DragDrop __v?.?.?__
- Windows OS __??__
- Visual Studio __20xx__
- .NET Framework __?.?__

View File

@@ -0,0 +1,5 @@
## What changed?
_Describe the changes you have made to improve this project._
_Closed issues._

261
Others/gong-wpf-dragdrop/.gitignore vendored Normal file
View File

@@ -0,0 +1,261 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# 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
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# scriptcs
**/scriptcs_packages/*
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
#.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
**/[tT]ools/*
!**/[tT]ools/[nN]uget/
!**/[tT]ools/XamlStyler/
!**/[tT]ools/format_all_xaml.cmd

View File

@@ -0,0 +1,29 @@
create:
include-footer: true
footer-heading: Where to get it
footer-content: You can download this release from [gong-wpf-dragdrop](https://github.com/punker76/gong-wpf-dragdrop/releases/{milestone})
footer-includes-milestone: true
milestone-replace-text: '{milestone}'
export:
include-created-date-in-title: true
created-date-string-format: MMMM dd, yyyy
perform-regex-removal: true
regex-text: '### Where to get it(\r\n)*You can .*\)'
multiline-regex: true
issue-labels-include:
- Breaking Change
- Bug
- Feature
- Feature Request
- Enhancement
- Hacktoberfest
issue-labels-exclude:
- Internal Refactoring
- Build
- Question
- WontFix
- Duplicate
issue-labels-alias:
- name: Documentation
header: Documentation
plural: Documentation

View File

@@ -0,0 +1,20 @@
assembly-versioning-scheme: Major
assembly-file-versioning-scheme: MajorMinorPatchTag
mode: ContinuousDeployment
next-version: 2.0.0
branches:
master:
mode: ContinuousDeployment
tag: rc
prevent-increment-of-merged-branch-version: true
track-merge-target: false
is-release-branch: true
develop:
mode: ContinuousDeployment
tag: alpha
prevent-increment-of-merged-branch-version: true
track-merge-target: true
pull-request:
mode: ContinuousDelivery
ignore:
sha: []

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2015-2019, Jan Karger (Steven Kirk)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of gong-wpf-dragdrop nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,78 @@
<div align="center">
<a href="https://github.com/punker76/gong-wpf-dragdrop">
<img alt="gong-wpf-dragdrop" width="700" heigth="142" src="./GongSolutions.Wpf.DragDrop.Full.png">
</a>
<h1>GongSolutions.WPF.DragDrop</h1>
<p>
An easy to use drag'n'drop framework for WPF.
</p>
<a href="https://gitter.im/punker76/gong-wpf-dragdrop">
<img src="https://img.shields.io/badge/Gitter-Join%20Chat-green.svg?style=flat-square">
</a>
<a href="https://twitter.com/punker76">
<img src="https://img.shields.io/badge/twitter-%40punker76-55acee.svg?style=flat-square">
</a>
<a href="https://ci.appveyor.com/project/punker76/gong-wpf-dragdrop/branch/master">
<img alt="masterstatus" src="https://img.shields.io/appveyor/ci/punker76/gong-wpf-dragdrop/master.svg?style=flat-square&&label=master">
</a>
<a href="https://ci.appveyor.com/project/punker76/gong-wpf-dragdrop/branch/dev">
<img alt="devstatus" src="https://img.shields.io/appveyor/ci/punker76/gong-wpf-dragdrop/dev.svg?style=flat-square&&label=dev">
</a>
<a href="https://github.com/punker76/gong-wpf-dragdrop/releases/latest">
<img src="https://img.shields.io/github/release/punker76/gong-wpf-dragdrop.svg?style=flat-square">
</a>
<br />
<a href="https://www.nuget.org/packages/gong-wpf-dragdrop">
<img src="https://img.shields.io/nuget/dt/gong-wpf-dragdrop.svg?style=flat-square">
</a>
<a href="https://www.nuget.org/packages/gong-wpf-dragdrop">
<img src="https://img.shields.io/nuget/v/gong-wpf-dragdrop.svg?style=flat-square">
</a>
<a href="https://www.nuget.org/packages/gong-wpf-dragdrop">
<img src="https://img.shields.io/nuget/vpre/gong-wpf-dragdrop.svg?style=flat-square&label=nuget-pre">
</a>
<a href="https://github.com/punker76/gong-wpf-dragdrop/issues">
<img src="https://img.shields.io/github/issues/punker76/gong-wpf-dragdrop.svg?style=flat-square">
</a>
<br />
<br />
</div>
## Features
+ Works with MVVM : the logic for the drag and drop can be placed in a ViewModel. No code needs to be placed in codebehind, instead attached properties are used to bind to a drag handler/drop handler in a ViewModel.
+ Works with multiple selections.
+ Can drag data within the same control to re-order, or between controls.
+ Works with `ListBox`, `ListView`, `TreeView`, `DataGrid` and any other `ItemsControl`.
+ Can insert, move or copy an item into a collection of the same control or into another.
+ Can display Adorners to give the user visual feedback of the operation in progress.
+ Has sensible defaults so that you have to write less code for common operations.
## Let's get started
- [Building](../../wiki/Building) the `gong-wpf-dragdrop` solution
- [How to...](../../wiki/Usage)
- [Releases and Release Notes](../../releases)
- [Release History](../../wiki/Release-History)
- [Strong naming](../../wiki/Strong-naming)
- [Wiki](../../wiki)
- [License](https://github.com/punker76/gong-wpf-dragdrop/blob/dev/LICENSE)
## Want to say thanks?
This framework is free and can be used for free, open source and commercial applications. It's tested and contributed by many people... So mainly hit the :star: button, that's all... thx :squirrel: (:dollar:, :euro:, :beer: or some other gifts are also being accepted...).
## In action
![screenshot01](./screenshots/2016-09-03_00h51_35.png)
![screenshot02](./screenshots/2016-09-03_00h52_20.png)
![screenshot03](./screenshots/2016-09-03_00h53_03.png)
![screenshot04](./screenshots/2016-09-03_00h53_21.png)
![gif01](./screenshots/DragDropSample01.gif)

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,753 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Irony</name>
</assembly>
<members>
<member name="T:Irony.Parsing.PrecedenceHint">
<summary> A hint to use precedence. </summary>
<remarks>
Not used directly in grammars; injected automatically by system in states having conflicts on operator symbols.
The purpose of the hint is make handling precedence similar to other conflict resolution methods - through hints
activated during parser construction. The hint code analyzes the conflict and resolves it by adding custom or general action
for a conflicting input.
</remarks>
</member>
<member name="T:Irony.Parsing.ReduceParserAction">
<summary>Base class for more specific reduce actions. </summary>
</member>
<member name="M:Irony.Parsing.ReduceParserAction.Create(Irony.Parsing.Production)">
<summary>Factory method for creating a proper type of reduce parser action. </summary>
<param name="production">A Production to reduce.</param>
<returns>Reduce action.</returns>
</member>
<member name="T:Irony.Parsing.ReduceTransientParserAction">
<summary>Reduces non-terminal marked as Transient by MarkTransient method. </summary>
</member>
<member name="T:Irony.Parsing.ReduceListBuilderParserAction">
<summary>Reduces list created by MakePlusRule or MakeListRule methods. </summary>
</member>
<member name="T:Irony.Parsing.ISourceStream">
<summary>
Interface for Terminals to access the source stream and produce tokens.
</summary>
</member>
<member name="P:Irony.Parsing.ISourceStream.Text">
<summary>
Returns the source text
</summary>
</member>
<member name="P:Irony.Parsing.ISourceStream.Location">
<summary>
Gets or sets the start location (position, row, column) of the new token
</summary>
</member>
<member name="P:Irony.Parsing.ISourceStream.Position">
<summary>
Gets or sets the current position in the source file. When reading the value, returns Location.Position value.
When a new value is assigned, the Location is modified accordingly.
</summary>
</member>
<member name="P:Irony.Parsing.ISourceStream.PreviewPosition">
<summary>
Gets or sets the current preview position in the source file. Must be greater or equal to Location.Position
</summary>
</member>
<member name="P:Irony.Parsing.ISourceStream.PreviewChar">
<summary>
Gets a char at preview position
</summary>
</member>
<member name="P:Irony.Parsing.ISourceStream.NextPreviewChar">
<summary>
Gets the char at position next after the PrevewPosition
</summary>
</member>
<member name="M:Irony.Parsing.ISourceStream.CreateToken(Irony.Parsing.Terminal)">
<summary>
Creates a new token based on current preview position.
</summary>
<param name="terminal">A terminal associated with the token.</param>
<returns>New token.</returns>
</member>
<member name="M:Irony.Parsing.ISourceStream.CreateToken(Irony.Parsing.Terminal,System.Object)">
<summary>
Creates a new token based on current preview position and sets its Value field.
</summary>
<param name="terminal">A terminal associated with the token.</param>
<param name="value">The value associated with the token.</param>
<returns>New token.</returns>
</member>
<member name="M:Irony.Parsing.ISourceStream.MatchSymbol(System.String)">
<summary>Tries to match the symbol with the text at current preview position. </summary>
<param name="symbol">A symbol to match</param>
<returns>True if there is a match; otherwise, false.</returns>
</member>
<member name="M:Irony.Parsing.ParseTreeNode.IsPunctuationOrEmptyTransient">
<summary>Returns true if the node is punctuation or it is transient with empty child list.</summary>
<returns>True if parser can safely ignore this node.</returns>
</member>
<member name="F:Irony.Parsing.SourceLocation.Line">
<summary>Source line number, 0-based.</summary>
</member>
<member name="F:Irony.Parsing.SourceLocation.Column">
<summary>Source column number, 0-based.</summary>
</member>
<member name="M:Irony.Parsing.GrammarHint.Apply(Irony.Parsing.LanguageData,Irony.Parsing.Construction.LRItem)">
<summary> Gives a chance to a custom code in hint to interfere in parser automaton construction.</summary>
<param name="language">The LanguageData instance.</param>
<param name="owner">The LRItem that "owns" the hint. </param>
<remarks>
The most common purpose of this method (it's overrides) is to resolve the conflicts
by adding specific actions into State.Actions dictionary.
The owner parameter represents the position in the grammar expression where the hint
is found. The parser state is available through owner.State property.
</remarks>
</member>
<member name="F:Irony.Parsing.Grammar.CaseSensitive">
<summary>
Gets case sensitivity of the grammar. Read-only, true by default.
Can be set to false only through a parameter to grammar constructor.
</summary>
</member>
<member name="F:Irony.Parsing.Grammar.Root">
<summary>
The main root entry for the grammar.
</summary>
</member>
<member name="F:Irony.Parsing.Grammar.SnippetRoots">
<summary>
Alternative roots for parsing code snippets.
</summary>
</member>
<member name="M:Irony.Parsing.Grammar.OnScannerSelectTerminal(Irony.Parsing.ParsingContext)">
<summary>
Override this method to help scanner select a terminal to create token when there are more than one candidates
for an input char. context.CurrentTerminals contains candidate terminals; leave a single terminal in this list
as the one to use.
</summary>
</member>
<member name="M:Irony.Parsing.Grammar.SkipWhitespace(Irony.Parsing.ISourceStream)">
<summary>Skips whitespace characters in the input stream. </summary>
<remarks>Override this method if your language has non-standard whitespace characters.</remarks>
<param name="source">Source stream.</param>
</member>
<member name="M:Irony.Parsing.Grammar.IsWhitespaceOrDelimiter(System.Char)">
<summary>Returns true if a character is whitespace or delimiter. Used in quick-scanning versions of some terminals. </summary>
<param name="ch">The character to check.</param>
<returns>True if a character is whitespace or delimiter; otherwise, false.</returns>
<remarks>Does not have to be completely accurate, should recognize most common characters that are special chars by themselves
and may never be part of other multi-character tokens. </remarks>
</member>
<member name="M:Irony.Parsing.Grammar.AddTermsReportGroup(System.String,System.String[])">
<summary>
Creates a terminal reporting group, so all terminals in the group will be reported as a single "alias" in syntex error messages like
"Syntax error, expected: [list of terms]"
</summary>
<param name="alias">An alias for all terminals in the group.</param>
<param name="symbols">Symbols to be included into the group.</param>
</member>
<member name="M:Irony.Parsing.Grammar.AddTermsReportGroup(System.String,Irony.Parsing.Terminal[])">
<summary>
Creates a terminal reporting group, so all terminals in the group will be reported as a single "alias" in syntex error messages like
"Syntax error, expected: [list of terms]"
</summary>
<param name="alias">An alias for all terminals in the group.</param>
<param name="terminals">Terminals to be included into the group.</param>
</member>
<member name="M:Irony.Parsing.Grammar.AddToNoReportGroup(System.String[])">
<summary>
Adds symbols to a group with no-report type, so symbols will not be shown in expected lists in syntax error messages.
</summary>
<param name="symbols">Symbols to exclude.</param>
</member>
<member name="M:Irony.Parsing.Grammar.AddToNoReportGroup(Irony.Parsing.Terminal[])">
<summary>
Adds symbols to a group with no-report type, so symbols will not be shown in expected lists in syntax error messages.
</summary>
<param name="symbols">Symbols to exclude.</param>
</member>
<member name="M:Irony.Parsing.Grammar.AddOperatorReportGroup(System.String)">
<summary>
Adds a group and an alias for all operator symbols used in the grammar.
</summary>
<param name="alias">An alias for operator symbols.</param>
</member>
<member name="T:Irony.Resources">
<summary>
A strongly-typed resource class, for looking up localized strings, etc.
</summary>
</member>
<member name="P:Irony.Resources.ResourceManager">
<summary>
Returns the cached ResourceManager instance used by this class.
</summary>
</member>
<member name="P:Irony.Resources.Culture">
<summary>
Overrides the current thread's CurrentUICulture property for all
resource lookups using this strongly typed resource class.
</summary>
</member>
<member name="P:Irony.Resources.ConsoleNoChars">
<summary>
Looks up a localized string similar to Nn.
</summary>
</member>
<member name="P:Irony.Resources.ConsoleYesChars">
<summary>
Looks up a localized string similar to Yy.
</summary>
</member>
<member name="P:Irony.Resources.ErrAmbigGrammarRR">
<summary>
Looks up a localized string similar to Ambiguous grammar, unresolvable reduce-reduce conflicts. State {0}, lookaheads [{1}].
</summary>
</member>
<member name="P:Irony.Resources.ErrAmbigGrammarSR">
<summary>
Looks up a localized string similar to Ambiguous grammar, unresolvable shift-reduce conflicts. State {0}, lookaheads [{1}].
</summary>
</member>
<member name="P:Irony.Resources.ErrArgListNotFound">
<summary>
Looks up a localized string similar to Argument list not found in the stack. Expected: ValueList, found: {0}..
</summary>
</member>
<member name="P:Irony.Resources.ErrAssignLiteralValue">
<summary>
Looks up a localized string similar to Invalid operation, attempt to assign to a constant or literal value..
</summary>
</member>
<member name="P:Irony.Resources.ErrBadChar">
<summary>
Looks up a localized string similar to Invalid length of char literal - should be a single character..
</summary>
</member>
<member name="P:Irony.Resources.ErrBadStrLiteral">
<summary>
Looks up a localized string similar to Mal-formed string literal - cannot find termination symbol..
</summary>
</member>
<member name="P:Irony.Resources.ErrBadUnEscape">
<summary>
Looks up a localized string similar to Invalid unicode escape ({0}), expected {1} hex digits..
</summary>
</member>
<member name="P:Irony.Resources.ErrBadXEscape">
<summary>
Looks up a localized string similar to Invalid \x escape, at least one digit expected..
</summary>
</member>
<member name="P:Irony.Resources.ErrCannotConvertValue">
<summary>
Looks up a localized string similar to Cannot convert value from type {0} to type {1}, type converter not defined..
</summary>
</member>
<member name="P:Irony.Resources.ErrCannotConvertValueToType">
<summary>
Looks up a localized string similar to Cannot convert literal {0} to type {1}..
</summary>
</member>
<member name="P:Irony.Resources.ErrCannotMoveBackInSource">
<summary>
Looks up a localized string similar to Fatal error in parser: attempt to move back in the source..
</summary>
</member>
<member name="P:Irony.Resources.ErrConflictMsgTemplate">
<summary>
Looks up a localized string similar to {0} State {1} on inputs: {2}.
</summary>
</member>
<member name="P:Irony.Resources.ErrConsoleFatalError">
<summary>
Looks up a localized string similar to Fatal error:.
</summary>
</member>
<member name="P:Irony.Resources.ErrConstructNotSupported">
<summary>
Looks up a localized string similar to Construct &apos;{0}&apos; is not supported (yet) by language implementation..
</summary>
</member>
<member name="P:Irony.Resources.ErrDsvNoClosingQuote">
<summary>
Looks up a localized string similar to Could not find a closing quote for quoted value..
</summary>
</member>
<member name="P:Irony.Resources.ErrDupRegexSwitch">
<summary>
Looks up a localized string similar to Duplicate switch &apos;{0}&apos; for regular expression..
</summary>
</member>
<member name="P:Irony.Resources.ErrDupStartSymbolStr">
<summary>
Looks up a localized string similar to Duplicate start symbol {0} in string literal [{1}]..
</summary>
</member>
<member name="P:Irony.Resources.ErrFailedCreateNode">
<summary>
Looks up a localized string similar to Failed to create AST node for non-terminal [{0}], error: {1}.
</summary>
</member>
<member name="P:Irony.Resources.ErrFreeTextNoEndTag">
<summary>
Looks up a localized string similar to Failed to find the ending tag &apos;{0}&apos; for a text literal. .
</summary>
</member>
<member name="P:Irony.Resources.ErrImpliedOpUseParserLink">
<summary>
Looks up a localized string similar to ImpliedSymbolTerminal cannot be used in grammar with DisableScannerParserLink flag set.
</summary>
</member>
<member name="P:Irony.Resources.ErrInternalErrDataPopFailed">
<summary>
Looks up a localized string similar to Interpreter error, DataStack.Pop() operation failed - stack is empty..
</summary>
</member>
<member name="P:Irony.Resources.ErrInterpreterIsBusy">
<summary>
Looks up a localized string similar to Interpreter is busy..
</summary>
</member>
<member name="P:Irony.Resources.ErrInvalidArgsForIncDec">
<summary>
Looks up a localized string similar to Invalid arguments for IncDecNode AST node: either first or second argument should be &apos;--&apos; or &apos;++&apos;..
</summary>
</member>
<member name="P:Irony.Resources.ErrInvalidAstMode">
<summary>
Looks up a localized string similar to Invalid AstMode value in call to Evaluate method. Node: {0}, mode: {1}..
</summary>
</member>
<member name="P:Irony.Resources.ErrInvalidChar">
<summary>
Looks up a localized string similar to Invalid character: &apos;{0}&apos;..
</summary>
</member>
<member name="P:Irony.Resources.ErrInvalidEmbeddedPrefix">
<summary>
Looks up a localized string similar to Invalid embedded expression. .
</summary>
</member>
<member name="P:Irony.Resources.ErrInvDedent">
<summary>
Looks up a localized string similar to Invalid dedent level, no previous matching indent found..
</summary>
</member>
<member name="P:Irony.Resources.ErrInvEscape">
<summary>
Looks up a localized string similar to Invalid escape sequence: \{0}..
</summary>
</member>
<member name="P:Irony.Resources.ErrInvEscSeq">
<summary>
Looks up a localized string similar to Invalid escape sequence..
</summary>
</member>
<member name="P:Irony.Resources.ErrInvEscSymbol">
<summary>
Looks up a localized string similar to Invalid escape symbol, expected &apos;u&apos; or &apos;U&apos; only..
</summary>
</member>
<member name="P:Irony.Resources.ErrInvNumber">
<summary>
Looks up a localized string similar to Invalid number..
</summary>
</member>
<member name="P:Irony.Resources.ErrInvRegexSwitch">
<summary>
Looks up a localized string similar to Invalid switch &apos;{0}&apos; for regular expression.
</summary>
</member>
<member name="P:Irony.Resources.ErrInvStrDef">
<summary>
Looks up a localized string similar to Error in string literal [{0}]: No start/end symbols specified..
</summary>
</member>
<member name="P:Irony.Resources.ErrLastTermOfErrorProd">
<summary>
Looks up a localized string similar to The last term of production containing SyntaxError must be a terminal. NonTerminal: {0}.
</summary>
</member>
<member name="P:Irony.Resources.ErrListCannotBeTransient">
<summary>
Looks up a localized string similar to List non-terminals cannot be marked transient; list: ({0}).
</summary>
</member>
<member name="P:Irony.Resources.ErrNewLineExpected">
<summary>
Looks up a localized string similar to Expected new line symbol..
</summary>
</member>
<member name="P:Irony.Resources.ErrNoClosingBrace">
<summary>
Looks up a localized string similar to No closing pair for opening symbol {0}.
</summary>
</member>
<member name="P:Irony.Resources.ErrNodeTypeNotSetOn">
<summary>
Looks up a localized string similar to AstNodeType or AstNodeCreator is not set on non-terminals: {0}. Either set Term.AstConfig.NodeType, or provide default values in AstContext..
</summary>
</member>
<member name="P:Irony.Resources.ErrNoEndForRegex">
<summary>
Looks up a localized string similar to No end symbol for regex literal..
</summary>
</member>
<member name="P:Irony.Resources.ErrNoEndTagInEmbExpr">
<summary>
Looks up a localized string similar to No ending tag &apos;{0}&apos; found in embedded expression..
</summary>
</member>
<member name="P:Irony.Resources.ErrNoImplForUnaryOp">
<summary>
Looks up a localized string similar to UnExprNode: no implementation for unary operator &apos;{0}&apos;..
</summary>
</member>
<member name="P:Irony.Resources.ErrNoLetterAfterNum">
<summary>
Looks up a localized string similar to Number cannot be followed by a letter..
</summary>
</member>
<member name="P:Irony.Resources.ErrNoLkhds">
<summary>
Looks up a localized string similar to ParserDataBuilder error: inadequate state {0}, reduce item &apos;{1}&apos; has no lookaheads..
</summary>
</member>
<member name="P:Irony.Resources.ErrNtRuleIsNull">
<summary>
Looks up a localized string similar to Non-terminal {0} has uninitialized Rule property..
</summary>
</member>
<member name="P:Irony.Resources.ErrNullNodeEval">
<summary>
Looks up a localized string similar to Attempt to evaluate NULL AST node. The AST node for term &apos;{0}&apos; was not created during parsing..
</summary>
</member>
<member name="P:Irony.Resources.ErrOpNotDefinedForType">
<summary>
Looks up a localized string similar to Operator &apos;{0}&apos; is not defined for types {1}..
</summary>
</member>
<member name="P:Irony.Resources.ErrOpNotDefinedForTypes">
<summary>
Looks up a localized string similar to Operator &apos;{0}&apos; is not defined for types {1} and {2}..
</summary>
</member>
<member name="P:Irony.Resources.ErrOpNotImplemented">
<summary>
Looks up a localized string similar to Operator &apos;{0} not imlemented..
</summary>
</member>
<member name="P:Irony.Resources.ErrOutErrorPrintFormat">
<summary>
Looks up a localized string similar to {0}: {1}.
</summary>
</member>
<member name="P:Irony.Resources.ErrOutlineFilterContSymbol">
<summary>
Looks up a localized string similar to CodeOutlineFilter: line continuation symbol &apos;{0}&apos; should be added to Grammar.NonGrammarTerminals list..
</summary>
</member>
<member name="P:Irony.Resources.ErrParserUnexpectedInput">
<summary>
Looks up a localized string similar to Syntax error, unexpected input..
</summary>
</member>
<member name="P:Irony.Resources.ErrParseTreeNull">
<summary>
Looks up a localized string similar to Parsed tree is null, cannot evaluate..
</summary>
</member>
<member name="P:Irony.Resources.ErrParseTreeRootNull">
<summary>
Looks up a localized string similar to Parse tree root is null, cannot evaluate..
</summary>
</member>
<member name="P:Irony.Resources.ErrRootAstNodeNull">
<summary>
Looks up a localized string similar to Root AST node is null, cannot evaluate..
</summary>
</member>
<member name="P:Irony.Resources.ErrRootAstNoInterface">
<summary>
Looks up a localized string similar to Root AST node does not implement IInterpretedAstNode interface, cannot evaluate..
</summary>
</member>
<member name="P:Irony.Resources.ErrRootNotRegistered">
<summary>
Looks up a localized string similar to ({0}) term passed as &apos;root&apos; paramater to parserr is not Root or snippet root of the grammar. Add it to SnippetRoots set in grammar constructor..
</summary>
</member>
<member name="P:Irony.Resources.ErrRootNotSet">
<summary>
Looks up a localized string similar to Root property of the grammar is not set..
</summary>
</member>
<member name="P:Irony.Resources.ErrRRConflict">
<summary>
Looks up a localized string similar to Reduce-reduce conflict. State {0}, lookaheads: {1}. Selected reduce on first production in conflict set..
</summary>
</member>
<member name="P:Irony.Resources.ErrRuleContainsNull">
<summary>
Looks up a localized string similar to Rule for NonTerminal {0} contains null as an operand in position {1} in one of productions..
</summary>
</member>
<member name="P:Irony.Resources.ErrSRConflict">
<summary>
Looks up a localized string similar to Shift-reduce conflict. State {0}, lookaheads [{1}]. Selected shift as preferred action..
</summary>
</member>
<member name="P:Irony.Resources.ErrSyntaxErrorExpected">
<summary>
Looks up a localized string similar to Syntax error, expected: {0}.
</summary>
</member>
<member name="P:Irony.Resources.ErrSyntaxErrorNoInfo">
<summary>
Looks up a localized string similar to Syntax error..
</summary>
</member>
<member name="P:Irony.Resources.ErrTemplExprNotRoot">
<summary>
Looks up a localized string similar to Expression root non-terminal in template settings (AstNodeConfig property) in templated string literal [{0}] is not added to Roots set. Add it to SnippetRoots in grammar constructor..
</summary>
</member>
<member name="P:Irony.Resources.ErrTemplMissingExprRoot">
<summary>
Looks up a localized string similar to Expression root is not specified in template settings (AstNodeConfig property) in templated string literal [{0}]. .
</summary>
</member>
<member name="P:Irony.Resources.ErrTemplNoSettings">
<summary>
Looks up a localized string similar to Error in string literal [{0}]: IsTemplate flag is set, but TemplateSettings is not provided in AstNodeConfig property..
</summary>
</member>
<member name="P:Irony.Resources.ErrTerminalHasEmptyPrefix">
<summary>
Looks up a localized string similar to A terminal {0} has empty prefix..
</summary>
</member>
<member name="P:Irony.Resources.ErrTransientNtMustHaveOneTerm">
<summary>
Looks up a localized string similar to Transient non-terminal must have zero or one non-punctuation child nodes; non-terminals: {0}..
</summary>
</member>
<member name="P:Irony.Resources.ErrUnclosedComment">
<summary>
Looks up a localized string similar to Unclosed comment block.
</summary>
</member>
<member name="P:Irony.Resources.ErrUnexpEof">
<summary>
Looks up a localized string similar to Unexpected end of file..
</summary>
</member>
<member name="P:Irony.Resources.ErrUnexpIndent">
<summary>
Looks up a localized string similar to Unexpected indentation..
</summary>
</member>
<member name="P:Irony.Resources.ErrUnmatchedCloseBrace">
<summary>
Looks up a localized string similar to Unmatched closing brace &apos;{0}&apos;..
</summary>
</member>
<member name="P:Irony.Resources.ErrVarIsNotCallable">
<summary>
Looks up a localized string similar to Variable {0} is not a callable function..
</summary>
</member>
<member name="P:Irony.Resources.ErrVarNotDefined">
<summary>
Looks up a localized string similar to Variable {0} not defined..
</summary>
</member>
<member name="P:Irony.Resources.ErrWrongArgCount">
<summary>
Looks up a localized string similar to Invalid number of arguments. Expected {0}, found {1}..
</summary>
</member>
<member name="P:Irony.Resources.FmtRowCol">
<summary>
Looks up a localized string similar to ({0}:{1}).
</summary>
</member>
<member name="P:Irony.Resources.LabelActionAccept">
<summary>
Looks up a localized string similar to Accept.
</summary>
</member>
<member name="P:Irony.Resources.LabelActionOp">
<summary>
Looks up a localized string similar to Operator, shift to {0}/reduce on {1}..
</summary>
</member>
<member name="P:Irony.Resources.LabelActionReduce">
<summary>
Looks up a localized string similar to Reduce on {0}.
</summary>
</member>
<member name="P:Irony.Resources.LabelActionShift">
<summary>
Looks up a localized string similar to Shift to {0}.
</summary>
</member>
<member name="P:Irony.Resources.LabelActionUnknown">
<summary>
Looks up a localized string similar to (Unknown action type).
</summary>
</member>
<member name="P:Irony.Resources.LabelEofMark">
<summary>
Looks up a localized string similar to (EOF).
</summary>
</member>
<member name="P:Irony.Resources.LabelEosLabel">
<summary>
Looks up a localized string similar to [end-of-statement].
</summary>
</member>
<member name="P:Irony.Resources.LabelInitialState">
<summary>
Looks up a localized string similar to (INITIAL STATE).
</summary>
</member>
<member name="P:Irony.Resources.LabelKeySymbol">
<summary>
Looks up a localized string similar to (Key symbol).
</summary>
</member>
<member name="P:Irony.Resources.LabelKeyword">
<summary>
Looks up a localized string similar to (Keyword).
</summary>
</member>
<member name="P:Irony.Resources.LabelLineBreak">
<summary>
Looks up a localized string similar to [line break].
</summary>
</member>
<member name="P:Irony.Resources.LabelLocation">
<summary>
Looks up a localized string similar to Location:.
</summary>
</member>
<member name="P:Irony.Resources.LabelNone">
<summary>
Looks up a localized string similar to None.
</summary>
</member>
<member name="P:Irony.Resources.LabelSrcHaveMore">
<summary>
Looks up a localized string similar to ....
</summary>
</member>
<member name="P:Irony.Resources.LabelUnnamed">
<summary>
Looks up a localized string similar to (unnamed).
</summary>
</member>
<member name="P:Irony.Resources.MsgAbortScriptYN">
<summary>
Looks up a localized string similar to Abort script(y/n)?.
</summary>
</member>
<member name="P:Irony.Resources.MsgDefaultConsoleGreeting">
<summary>
Looks up a localized string similar to {0} Console.\r\nPress Ctrl-C to exit the program.\r\n.
</summary>
</member>
<member name="P:Irony.Resources.MsgDefaultConsoleTitle">
<summary>
Looks up a localized string similar to Console.
</summary>
</member>
<member name="P:Irony.Resources.MsgErrorCustomActionDidNotAdvance">
<summary>
Looks up a localized string similar to Custom action did not execute: parser state or input did not change..
</summary>
</member>
<member name="P:Irony.Resources.MsgExitConsoleYN">
<summary>
Looks up a localized string similar to Exit console (y/n)?.
</summary>
</member>
<member name="P:Irony.Resources.MsgNLALRAdvice">
<summary>
Looks up a localized string similar to NLALR transform: Add WrapTail() in &apos;.&apos; position to [{0}]..
</summary>
</member>
<member name="P:Irony.Resources.MsgPressAnyKeyToExit">
<summary>
Looks up a localized string similar to Press any key to end the program..
</summary>
</member>
<member name="P:Irony.Resources.MsgSrcPosToString">
<summary>
Looks up a localized string similar to &quot;[{0}], at {1}.
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceConflictResolved">
<summary>
Looks up a localized string similar to Parsing conflict resolved in code..
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceExecCustomAction">
<summary>
Looks up a localized string similar to Executing custom action.
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceOpResolved">
<summary>
Looks up a localized string similar to Operator - resolved to {0}.
</summary>
</member>
<member name="P:Irony.Resources.MsgTracePoppedState">
<summary>
Looks up a localized string similar to Popped state from stack, pushing {0}.
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceRecoverAction">
<summary>
Looks up a localized string similar to RECOVERING: {0}.
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceRecoverFailed">
<summary>
Looks up a localized string similar to FAILED TO RECOVER.
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceRecoverFoundState">
<summary>
Looks up a localized string similar to RECOVERING: Found state with shift on error : {0}.
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceRecovering">
<summary>
Looks up a localized string similar to RECOVERING: popping stack, looking for state with error shift.
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceRecoverReducing">
<summary>
Looks up a localized string similar to RECOVERING: Reached end of error production, reducing..
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceRecoverShiftError">
<summary>
Looks up a localized string similar to RECOVERING: Shifting Error term, {0}.
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceRecoverShiftTillEnd">
<summary>
Looks up a localized string similar to RECOVERING: shifting until the end of error production..
</summary>
</member>
<member name="P:Irony.Resources.MsgTraceRecoverSuccess">
<summary>
Looks up a localized string similar to RECOVERED.
</summary>
</member>
</members>
</doc>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
{
"AttributesTolerance": 2,
"KeepFirstAttributeOnSameLine": false,
"MaxAttributeCharactersPerLine": 0,
"MaxAttributesPerLine": 1,
"NewlineExemptionElements": "RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransfom, SkewTransform, RotateTransform, TranslateTransform, Trigger, Condition, Setter",
"SeparateByGroups": false,
"AttributeIndentation": 0,
"AttributeIndentationStyle": 1,
"RemoveDesignTimeReferences": false,
"EnableAttributeReordering": true,
"AttributeOrderingRuleGroups": [
"x:Class",
"xmlns, xmlns:x",
"xmlns:*",
"x:Key, Key, x:Name, Name, x:Uid, Uid, Title",
"Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom",
"Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight",
"Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex",
"*:*, *",
"PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint",
"mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText",
"Storyboard.*, From, To, Duration"
],
"FirstLineAttributes": "",
"OrderAttributesByName": true,
"PutEndingBracketOnNewLine": false,
"RemoveEndingTagOfEmptyElement": true,
"SpaceBeforeClosingSlash": true,
"RootElementLineBreakRule": 0,
"ReorderVSM": 2,
"ReorderGridChildren": false,
"ReorderCanvasChildren": false,
"ReorderSetters": 0,
"FormatMarkupExtension": true,
"NoNewLineMarkupExtensions": "x:Bind, Binding",
"ThicknessSeparator": 2,
"ThicknessAttributes": "Margin, Padding, BorderThickness, ThumbnailClipMargin",
"FormatOnSave": true,
"CommentPadding": 2,
}

View File

@@ -0,0 +1,42 @@
{
"IndentSize": 4,
"AttributesTolerance": 2,
"KeepFirstAttributeOnSameLine": true,
"MaxAttributeCharatersPerLine": 0,
"MaxAttributesPerLine": 1,
"NewlineExemptionElements": "RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransfom, SkewTransform, RotateTransform, TranslateTransform, Trigger, MultiTrigger, DataTrigger, MultiDataTrigger, Condition, Setter, SolidColorBrush, LinearGradientBrush",
"SeparateByGroups": false,
"AttributeIndentation": 0,
"AttributeIndentationStyle": 1,
"RemoveDesignTimeReferences": false,
"EnableAttributeReordering": true,
"AttributeOrderingRuleGroups": [
"x:Class",
"xmlns, xmlns:x",
"xmlns:*",
"x:Key, Key, x:Name, Name, x:Uid, Uid, Title",
"Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom",
"Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight",
"Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex",
"*:*, *",
"PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint",
"*:Freeze, mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText",
"Storyboard.*, From, To, Duration"
],
"FirstLineAttributes": "",
"OrderAttributesByName": true,
"PutEndingBracketOnNewLine": false,
"RemoveEndingTagOfEmptyElement": true,
"SpaceBeforeClosingSlash": true,
"RootElementLineBreakRule": 1,
"ReorderVSM": 2,
"ReorderGridChildren": false,
"ReorderCanvasChildren": false,
"ReorderSetters": 3,
"FormatMarkupExtension": true,
"NoNewLineMarkupExtensions": "x:Bind, Binding, TemplateBinding, x:Static, DynamicResource, ComponentResourceKey",
"ThicknessSeparator": 1,
"ThicknessAttributes": "Margin, Padding, BorderThickness, ThumbnailClipMargin",
"FormatOnSave": false,
"CommentPadding": 2,
}

View File

@@ -0,0 +1 @@
.\xstyler.exe -r -d ..\src\Showcase\ -c .\Options\XAMLStyler.json

Binary file not shown.

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>

View File

@@ -0,0 +1,45 @@
# http://www.appveyor.com/docs/appveyor-yml
# configuration for develop/CI and master/Release branch
branches:
only:
- dev
- master
environment:
azure-key-vault-url:
secure: 1mKS/HfCVq+iYNRVSrrN8NEowOkKt3knrpMzw+SOy3g=
azure-key-vault-client-id:
secure: JfSqzmsJdXB6uIxttCRoQw1NygwxqXHDj9uIqQnWOb9VCnQYlRPlAnxgW0yTSX4b
azure-key-vault-client-secret:
secure: CUpRJxMLeUZwNPMcqI0wECaWfy5AMnWn1UZhBd9WnQ3Z16lJP1Vzrkf24mccbhUD
azure-key-vault-certificate:
secure: BSPdW2TgnQtoQXXbeDECug==
skip_tags: true
image: Visual Studio 2019
configuration: Release
test: off
install:
- cinst netfx-4.7-devpack
#- ps: Start-FileDownload 'https://dotnetcli.blob.core.windows.net/dotnet/Sdk/master/dotnet-sdk-latest-win-x64.exe'
#- ps: Start-Process .\dotnet-sdk-latest-win-x64.exe "/install /norestart /quiet /log sdkinstall.log" -NoNewWindow -Wait
#- ps: Start-FileDownload 'https://download.visualstudio.microsoft.com/download/pr/4032ceb5-61cd-495a-ab25-475aa2232f28/7eb614e777d87ef0d49f86be4fc8bbde/dotnet-sdk-3.0.100-preview4-011223-win-x64.exe'
#- ps: Start-Process .\dotnet-sdk-3.0.100-preview4-011223-win-x64.exe "/install /norestart /quiet /log sdkinstall.log" -NoNewWindow -Wait
# Install dotnet core 3.0 latest (alpha!!), as this is not provided on AppVeyor yet
- cinst dotnetcore-sdk --pre
pull_requests:
do_not_increment_build_number: false
build_script:
- ps: .\build.ps1 --bootstrap
- ps: .\build.ps1 -target appveyor
artifacts:
- path: \Publish\*.*
nuget:
disable_publish_on_pr: true

View File

@@ -0,0 +1,430 @@
//////////////////////////////////////////////////////////////////////
// TOOLS / ADDINS
//////////////////////////////////////////////////////////////////////
#module nuget:?package=Cake.DotNetTool.Module
#tool "dotnet:?package=NuGetKeyVaultSignTool&version=1.2.18"
#tool "dotnet:?package=AzureSignTool&version=2.0.17"
#tool GitVersion.CommandLine
#tool gitreleasemanager
#tool vswhere
#addin Cake.Figlet
//////////////////////////////////////////////////////////////////////
// ARGUMENTS
//////////////////////////////////////////////////////////////////////
var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");
var verbosity = Argument("verbosity", Verbosity.Minimal);
var dotnetcoreverbosity = Argument("dotnetcoreverbosity", DotNetCoreVerbosity.Minimal);
//////////////////////////////////////////////////////////////////////
// PREPARATION
//////////////////////////////////////////////////////////////////////
var repoName = "gong-wpf-dragdrop";
var isLocal = BuildSystem.IsLocalBuild;
// Set build version
if (isLocal == false || verbosity == Verbosity.Verbose)
{
GitVersion(new GitVersionSettings { OutputType = GitVersionOutput.BuildServer });
}
GitVersion gitVersion = GitVersion(new GitVersionSettings { OutputType = GitVersionOutput.Json });
var latestInstallationPath = VSWhereLatest(new VSWhereLatestSettings { IncludePrerelease = true });
var msBuildPath = latestInstallationPath.Combine("./MSBuild/Current/Bin");
var msBuildPathExe = msBuildPath.CombineWithFilePath("./MSBuild.exe");
if (FileExists(msBuildPathExe) == false)
{
throw new NotImplementedException("You need at least Visual Studio 2019 to build this project.");
}
var isPullRequest = AppVeyor.Environment.PullRequest.IsPullRequest;
var branchName = gitVersion.BranchName;
var isDevelopBranch = StringComparer.OrdinalIgnoreCase.Equals("dev", branchName);
var isReleaseBranch = StringComparer.OrdinalIgnoreCase.Equals("master", branchName);
var isTagged = AppVeyor.Environment.Repository.Tag.IsTag;
// Directories and Paths
var solution = "./src/GongSolutions.WPF.DragDrop.sln";
var publishDir = "./Publish";
// Define global marcos.
Action Abort = () => { throw new Exception("a non-recoverable fatal error occurred."); };
///////////////////////////////////////////////////////////////////////////////
// SETUP / TEARDOWN
///////////////////////////////////////////////////////////////////////////////
Setup(context =>
{
// Executed BEFORE the first task.
if (!IsRunningOnWindows())
{
throw new NotImplementedException($"{repoName} will only build on Windows because it's not possible to target WPF and Windows Forms from UNIX.");
}
Information(Figlet(repoName));
Information("Informational Version : {0}", gitVersion.InformationalVersion);
Information("SemVer Version : {0}", gitVersion.SemVer);
Information("AssemblySemVer Version : {0}", gitVersion.AssemblySemVer);
Information("MajorMinorPatch Version: {0}", gitVersion.MajorMinorPatch);
Information("NuGet Version : {0}", gitVersion.NuGetVersion);
Information("IsLocalBuild : {0}", isLocal);
Information("Branch : {0}", branchName);
Information("Configuration : {0}", configuration);
Information("MSBuildPath : {0}", msBuildPath);
});
Teardown(context =>
{
// Executed AFTER the last task.
});
//////////////////////////////////////////////////////////////////////
// TASKS
//////////////////////////////////////////////////////////////////////
Task("Clean")
.ContinueOnError()
.Does(() =>
{
var directoriesToDelete = GetDirectories("./**/obj")
.Concat(GetDirectories("./**/bin"))
.Concat(GetDirectories("./**/Publish"));
DeleteDirectories(directoriesToDelete, new DeleteDirectorySettings { Recursive = true, Force = true });
});
Task("Restore")
.Does(() =>
{
var msBuildSettings = new MSBuildSettings {
Verbosity = verbosity
, ToolPath = msBuildPathExe
, Configuration = configuration
, ArgumentCustomization = args => args.Append("/m")
};
MSBuild(solution, msBuildSettings.WithTarget("restore"));
});
Task("Build")
.IsDependentOn("Restore")
.Does(() =>
{
var msBuildSettings = new MSBuildSettings {
Verbosity = verbosity
, ToolPath = msBuildPathExe
, Configuration = configuration
, ArgumentCustomization = args => args.Append("/m")
, BinaryLogger = new MSBuildBinaryLogSettings() { Enabled = isLocal }
};
MSBuild(solution, msBuildSettings
.SetMaxCpuCount(0)
.WithProperty("Version", isReleaseBranch ? gitVersion.MajorMinorPatch : gitVersion.NuGetVersion)
.WithProperty("AssemblyVersion", gitVersion.AssemblySemVer)
.WithProperty("FileVersion", gitVersion.AssemblySemFileVer)
.WithProperty("InformationalVersion", gitVersion.InformationalVersion)
);
});
Task("dotnetBuild")
.Does(() =>
{
var buildSettings = new DotNetCoreBuildSettings {
Verbosity = dotnetcoreverbosity,
Configuration = configuration,
ArgumentCustomization = args => args.Append("/m"),
MSBuildSettings = new DotNetCoreMSBuildSettings()
.SetMaxCpuCount(0)
.SetConfiguration(configuration)
.WithProperty("Version", isReleaseBranch ? gitVersion.MajorMinorPatch : gitVersion.NuGetVersion)
.WithProperty("AssemblyVersion", gitVersion.AssemblySemVer)
.WithProperty("FileVersion", gitVersion.AssemblySemFileVer)
.WithProperty("InformationalVersion", gitVersion.InformationalVersion)
};
DotNetCoreBuild(solution, buildSettings);
});
Task("Pack")
.WithCriteria(() => !isPullRequest)
.Does(() =>
{
EnsureDirectoryExists(Directory(publishDir));
var msBuildSettings = new MSBuildSettings {
Verbosity = verbosity
, ToolPath = msBuildPathExe
, Configuration = configuration
};
var projects = GetFiles("./src/GongSolutions.WPF.DragDrop/*.csproj");
foreach(var project in projects)
{
Information("Packing {0}", project);
DeleteFiles(GetFiles("./src/**/*.nuspec"));
MSBuild(project, msBuildSettings
.WithTarget("pack")
.WithProperty("NoBuild", "true")
.WithProperty("IncludeBuildOutput", "true")
.WithProperty("PackageOutputPath", MakeAbsolute(Directory(publishDir)).FullPath)
.WithProperty("RepositoryBranch", branchName)
.WithProperty("RepositoryCommit", gitVersion.Sha)
.WithProperty("Version", isReleaseBranch ? gitVersion.MajorMinorPatch : gitVersion.NuGetVersion)
.WithProperty("AssemblyVersion", gitVersion.AssemblySemVer)
.WithProperty("FileVersion", gitVersion.AssemblySemFileVer)
.WithProperty("InformationalVersion", gitVersion.InformationalVersion)
);
}
});
Task("dotnetPack")
.WithCriteria(() => !isPullRequest)
.Does(() =>
{
EnsureDirectoryExists(Directory(publishDir));
var buildSettings = new DotNetCorePackSettings {
Verbosity = dotnetcoreverbosity,
Configuration = configuration,
NoRestore = true,
MSBuildSettings = new DotNetCoreMSBuildSettings()
.SetMaxCpuCount(0)
.SetConfiguration(configuration)
.WithProperty("NoBuild", "true")
.WithProperty("IncludeBuildOutput", "true")
.WithProperty("PackageOutputPath", MakeAbsolute(Directory(publishDir)).FullPath)
.WithProperty("RepositoryBranch", branchName)
.WithProperty("RepositoryCommit", gitVersion.Sha)
.WithProperty("Version", isReleaseBranch ? gitVersion.MajorMinorPatch : gitVersion.NuGetVersion)
.WithProperty("AssemblyVersion", gitVersion.AssemblySemVer)
.WithProperty("FileVersion", gitVersion.AssemblySemFileVer)
.WithProperty("InformationalVersion", gitVersion.InformationalVersion)
};
var projects = GetFiles("./src/GongSolutions.WPF.DragDrop/*.csproj");
foreach(var project in projects)
{
Information("Packing {0}", project);
DeleteFiles(GetFiles("./src/**/*.nuspec"));
DotNetCorePack(project.ToString(), buildSettings);
}
});
Task("Zip")
.Does(() =>
{
EnsureDirectoryExists(Directory(publishDir));
Zip($"./src/Showcase/bin/{configuration}", $"{publishDir}/Showcase.DragDrop.{configuration}-v" + gitVersion.NuGetVersion + ".zip");
});
Task("CreateRelease")
.WithCriteria(() => !isTagged)
.Does(() =>
{
var username = EnvironmentVariable("GITHUB_USERNAME");
if (string.IsNullOrEmpty(username))
{
throw new Exception("The GITHUB_USERNAME environment variable is not defined.");
}
var token = EnvironmentVariable("GITHUB_TOKEN");
if (string.IsNullOrEmpty(token))
{
throw new Exception("The GITHUB_TOKEN environment variable is not defined.");
}
GitReleaseManagerCreate(username, token, "punker76", repoName, new GitReleaseManagerCreateSettings {
Milestone = gitVersion.MajorMinorPatch,
Name = gitVersion.AssemblySemFileVer,
Prerelease = isDevelopBranch,
TargetCommitish = branchName,
WorkingDirectory = "."
});
});
void SignFiles(IEnumerable<FilePath> files, string description)
{
var vurl = EnvironmentVariable("azure-key-vault-url");
if(string.IsNullOrWhiteSpace(vurl)) {
Error("Could not resolve signing url.");
return;
}
var vcid = EnvironmentVariable("azure-key-vault-client-id");
if(string.IsNullOrWhiteSpace(vcid)) {
Error("Could not resolve signing client id.");
return;
}
var vcs = EnvironmentVariable("azure-key-vault-client-secret");
if(string.IsNullOrWhiteSpace(vcs)) {
Error("Could not resolve signing client secret.");
return;
}
var vc = EnvironmentVariable("azure-key-vault-certificate");
if(string.IsNullOrWhiteSpace(vc)) {
Error("Could not resolve signing certificate.");
return;
}
foreach(var file in files)
{
Information($"Sign file: {file}");
var processSettings = new ProcessSettings {
RedirectStandardOutput = true,
RedirectStandardError = true,
Arguments = new ProcessArgumentBuilder()
.Append("sign")
.Append(MakeAbsolute(file).FullPath)
.AppendSwitchQuoted("--file-digest", "sha256")
.AppendSwitchQuoted("--description", description)
.AppendSwitchQuoted("--description-url", "https://github.com/punker76/gong-wpf-dragdrop")
.Append("--no-page-hashing")
.AppendSwitchQuoted("--timestamp-rfc3161", "http://timestamp.digicert.com")
.AppendSwitchQuoted("--timestamp-digest", "sha256")
.AppendSwitchQuoted("--azure-key-vault-url", vurl)
.AppendSwitchQuotedSecret("--azure-key-vault-client-id", vcid)
.AppendSwitchQuotedSecret("--azure-key-vault-client-secret", vcs)
.AppendSwitchQuotedSecret("--azure-key-vault-certificate", vc)
};
using(var process = StartAndReturnProcess("tools/AzureSignTool", processSettings))
{
process.WaitForExit();
if (process.GetStandardOutput().Any())
{
Information($"Output:{Environment.NewLine}{string.Join(Environment.NewLine, process.GetStandardOutput())}");
}
if (process.GetStandardError().Any())
{
Information($"Errors occurred:{Environment.NewLine}{string.Join(Environment.NewLine, process.GetStandardError())}");
}
// This should output 0 as valid arguments supplied
Information("Exit code: {0}", process.GetExitCode());
}
}
}
Task("Sign")
.ContinueOnError()
.Does(() =>
{
var files = GetFiles("./src/GongSolutions.WPF.DragDrop/bin/**/*/GongSolutions.WPF.DragDrop.dll");
SignFiles(files, "GongSolutions.WPF.DragDrop, an easy to use drag'n'drop framework for WPF applications.");
files = GetFiles("./src/Showcase/bin/**/*/Showcase.WPF.DragDrop.exe");
SignFiles(files, "Demo application of GongSolutions.WPF.DragDrop, an easy to use drag'n'drop framework for WPF applications.");
});
Task("SignNuGet")
.ContinueOnError()
.Does(() =>
{
if (!DirectoryExists(Directory(publishDir)))
{
return;
}
var vurl = EnvironmentVariable("azure-key-vault-url");
if(string.IsNullOrWhiteSpace(vurl)) {
Error("Could not resolve signing url.");
return;
}
var vcid = EnvironmentVariable("azure-key-vault-client-id");
if(string.IsNullOrWhiteSpace(vcid)) {
Error("Could not resolve signing client id.");
return;
}
var vcs = EnvironmentVariable("azure-key-vault-client-secret");
if(string.IsNullOrWhiteSpace(vcs)) {
Error("Could not resolve signing client secret.");
return;
}
var vc = EnvironmentVariable("azure-key-vault-certificate");
if(string.IsNullOrWhiteSpace(vc)) {
Error("Could not resolve signing certificate.");
return;
}
var nugetFiles = GetFiles(publishDir + "/*.nupkg");
foreach(var file in nugetFiles)
{
Information($"Sign file: {file}");
var processSettings = new ProcessSettings {
RedirectStandardOutput = true,
RedirectStandardError = true,
Arguments = new ProcessArgumentBuilder()
.Append("sign")
.Append(MakeAbsolute(file).FullPath)
.Append("--force")
.AppendSwitchQuoted("--file-digest", "sha256")
.AppendSwitchQuoted("--timestamp-rfc3161", "http://timestamp.digicert.com")
.AppendSwitchQuoted("--timestamp-digest", "sha256")
.AppendSwitchQuoted("--azure-key-vault-url", vurl)
.AppendSwitchQuotedSecret("--azure-key-vault-client-id", vcid)
.AppendSwitchQuotedSecret("--azure-key-vault-client-secret", vcs)
.AppendSwitchQuotedSecret("--azure-key-vault-certificate", vc)
};
using(var process = StartAndReturnProcess("tools/NuGetKeyVaultSignTool", processSettings))
{
process.WaitForExit();
if (process.GetStandardOutput().Any())
{
Information($"Output:{Environment.NewLine}{string.Join(Environment.NewLine, process.GetStandardOutput())}");
}
if (process.GetStandardError().Any())
{
Information($"Errors occurred:{Environment.NewLine}{string.Join(Environment.NewLine, process.GetStandardError())}");
}
// This should output 0 as valid arguments supplied
Information("Exit code: {0}", process.GetExitCode());
}
}
});
///////////////////////////////////////////////////////////////////////////////
// TASK TARGETS
///////////////////////////////////////////////////////////////////////////////
Task("Default")
.IsDependentOn("Clean")
.IsDependentOn("Build");
// .IsDependentOn("dotnetBuild") // doesn't work with Fody
Task("appveyor")
.IsDependentOn("Default")
.IsDependentOn("Sign")
// .IsDependentOn("Pack");
.IsDependentOn("dotnetPack")
.IsDependentOn("SignNuGet")
.IsDependentOn("Zip")
;
///////////////////////////////////////////////////////////////////////////////
// EXECUTION
///////////////////////////////////////////////////////////////////////////////
RunTarget(target);

View File

@@ -0,0 +1,242 @@
##########################################################################
# This is the Cake bootstrapper script for PowerShell.
# This file was downloaded from https://github.com/cake-build/resources
# Feel free to change this file to fit your needs.
##########################################################################
<#
.SYNOPSIS
This is a Powershell script to bootstrap a Cake build.
.DESCRIPTION
This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
and execute your Cake build script with the parameters you provide.
.PARAMETER Script
The build script to execute.
.PARAMETER Target
The build script target to run.
.PARAMETER Configuration
The build configuration to use.
.PARAMETER Verbosity
Specifies the amount of information to be displayed.
.PARAMETER ShowDescription
Shows description about tasks.
.PARAMETER DryRun
Performs a dry run.
.PARAMETER SkipToolPackageRestore
Skips restoring of packages.
.PARAMETER ScriptArgs
Remaining arguments are added here.
.LINK
https://cakebuild.net
#>
[CmdletBinding()]
Param(
[string]$Script = "build.cake",
[string]$Target,
[string]$Configuration,
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
[string]$Verbosity,
[switch]$ShowDescription,
[Alias("WhatIf", "Noop")]
[switch]$DryRun,
[switch]$SkipToolPackageRestore,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$ScriptArgs
)
# Attempt to set highest encryption available for SecurityProtocol.
# PowerShell will not set this by default (until maybe .NET 4.6.x). This
# will typically produce a message for PowerShell v2 (just an info
# message though)
try {
# Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48)
# Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't
# exist in .NET 4.0, even though they are addressable if .NET 4.5+ is
# installed (.NET 4.5 is an in-place upgrade).
[System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48
} catch {
Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3'
}
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
function MD5HashFile([string] $filePath)
{
if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
{
return $null
}
[System.IO.Stream] $file = $null;
[System.Security.Cryptography.MD5] $md5 = $null;
try
{
$md5 = [System.Security.Cryptography.MD5]::Create()
$file = [System.IO.File]::OpenRead($filePath)
return [System.BitConverter]::ToString($md5.ComputeHash($file))
}
finally
{
if ($file -ne $null)
{
$file.Dispose()
}
}
}
function GetProxyEnabledWebClient
{
$wc = New-Object System.Net.WebClient
$proxy = [System.Net.WebRequest]::GetSystemWebProxy()
$proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
$wc.Proxy = $proxy
return $wc
}
Write-Host "Preparing to run build script..."
if(!$PSScriptRoot){
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
}
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins"
$MODULES_DIR = Join-Path $TOOLS_DIR "Modules"
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config"
$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config"
# Make sure tools folder exists
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
Write-Verbose -Message "Creating tools directory..."
New-Item -Path $TOOLS_DIR -Type directory | out-null
}
# Make sure that packages.config exist.
if (!(Test-Path $PACKAGES_CONFIG)) {
Write-Verbose -Message "Downloading packages.config..."
try {
$wc = GetProxyEnabledWebClient
$wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG)
} catch {
Throw "Could not download packages.config."
}
}
# Try find NuGet.exe in path if not exists
if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Trying to find nuget.exe in PATH..."
$existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) }
$NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
$NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
}
}
# Try download NuGet.exe if not exists
if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Downloading NuGet.exe..."
try {
$wc = GetProxyEnabledWebClient
$wc.DownloadFile($NUGET_URL, $NUGET_EXE)
} catch {
Throw "Could not download NuGet.exe."
}
}
# Save nuget.exe path to environment to be available to child processed
$ENV:NUGET_EXE = $NUGET_EXE
# Restore tools from NuGet?
if(-Not $SkipToolPackageRestore.IsPresent) {
Push-Location
Set-Location $TOOLS_DIR
# Check for changes in packages.config and remove installed tools if true.
[string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
Write-Verbose -Message "Missing or changed package.config hash..."
Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery |
Remove-Item -Recurse
}
Write-Verbose -Message "Restoring tools from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet tools."
}
else
{
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
}
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
}
# Restore addins from NuGet
if (Test-Path $ADDINS_PACKAGES_CONFIG) {
Push-Location
Set-Location $ADDINS_DIR
Write-Verbose -Message "Restoring addins from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet addins."
}
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
}
# Restore modules from NuGet
if (Test-Path $MODULES_PACKAGES_CONFIG) {
Push-Location
Set-Location $MODULES_DIR
Write-Verbose -Message "Restoring modules from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occurred while restoring NuGet modules."
}
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
}
# Make sure that Cake has been installed.
if (!(Test-Path $CAKE_EXE)) {
Throw "Could not find Cake.exe at $CAKE_EXE"
}
# Build Cake arguments
$cakeArguments = @("$Script");
if ($Target) { $cakeArguments += "-target=$Target" }
if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
if ($ShowDescription) { $cakeArguments += "-showdescription" }
if ($DryRun) { $cakeArguments += "-dryrun" }
$cakeArguments += $ScriptArgs
# Start Cake
Write-Host "Running build script..."
&$CAKE_EXE $cakeArguments
exit $LASTEXITCODE

View File

@@ -0,0 +1,16 @@
; This is the default configuration file for Cake.
; This file was downloaded from https://github.com/cake-build/resources
[Nuget]
Source=https://api.nuget.org/v3/index.json
UseInProcessClient=true
LoadDependencies=false
[Paths]
Tools=./tools
Addins=./tools/Addins
Modules=./tools/Modules
[Settings]
SkipVerification=false
SkipPackageVersionCheck=true

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

View File

@@ -0,0 +1,38 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Project properties -->
<PropertyGroup>
<TargetFrameworks>net45;net46;netcoreapp3.0</TargetFrameworks>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<LangVersion>latest</LangVersion>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoError>$(NoError);CS1591</NoError>
<UseWPF>true</UseWPF>
</PropertyGroup>
<!-- Add the references for all projects and targets -->
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.*" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<None Remove="**\*.png;**\*.jpg;**\*.ico" />
<Resource Include="**\*.png;**\*.jpg;**\*.ico" />
</ItemGroup>
<!-- SourceLink -->
<PropertyGroup>
<!-- Optional: Declare that the Repository URL can be published to NuSpec -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- Optional: Embed source files that are not tracked by the source control manager to the PDB -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Optional: Include PDB in the built .nupkg -->
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<!-- https://github.com/dotnet/sourcelink/blob/master/docs/README.md#embedallsources -->
<!-- <EmbedAllSources>true</EmbedAllSources>-->
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,21 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- AssemblyInfo -->
<PropertyGroup>
<Company>GongSolutions.WPF.DragDrop</Company>
<Product>GongSolutions.WPF.DragDrop</Product>
<Copyright>Copyright © 2013 - $([System.DateTime]::Today.ToString(yyyy)) GongSolutions.WPF.DragDrop</Copyright>
<Description>An easy to use drag'n'drop framework for WPF applications.</Description>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<Version>2.0.0.0</Version>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<FileVersion>2.0.0.0</FileVersion>
<InformationalVersion>2.0.0.0</InformationalVersion>
</PropertyGroup>
<ItemGroup>
<Page Update="@(Page)" SubType="Designer" Generator="MSBuild:Compile" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28729.10
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GongSolutions.WPF.DragDrop", "GongSolutions.WPF.DragDrop\GongSolutions.WPF.DragDrop.csproj", "{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Showcase.WPF.DragDrop", "Showcase\Showcase.WPF.DragDrop.csproj", "{4E15F482-1FE9-4614-98F7-31256B643462}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB5BFDCE-284B-4B1E-A99A-ED4764D26E74}.Release|Any CPU.Build.0 = Release|Any CPU
{4E15F482-1FE9-4614-98F7-31256B643462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E15F482-1FE9-4614-98F7-31256B643462}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E15F482-1FE9-4614-98F7-31256B643462}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E15F482-1FE9-4614-98F7-31256B643462}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4DA11A5C-612C-4901-AA97-FF5A87451FCA}
EndGlobalSection
EndGlobal

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections;
using System.Linq;
using System.Windows;
using GongSolutions.Wpf.DragDrop.Utilities;
namespace GongSolutions.Wpf.DragDrop
{
/// <summary>
/// The default drag handler for GongSolutions.Wpf.DragDrop.
/// </summary>
public class DefaultDragHandler : IDragSource
{
/// <summary>
/// Queries whether a drag can be started.
/// </summary>
/// <param name="dragInfo">Information about the drag.</param>
/// <remarks>
/// To allow a drag to be started, the <see cref="DragInfo.Effects" /> property on <paramref name="dragInfo" />
/// should be set to a value other than <see cref="DragDropEffects.None" />.
/// </remarks>
public virtual void StartDrag(IDragInfo dragInfo)
{
var items = TypeUtilities.CreateDynamicallyTypedList(dragInfo.SourceItems).Cast<object>().ToList();
if (items.Count > 1)
{
dragInfo.Data = items;
}
else
{
// special case: if the single item is an enumerable then we can not drop it as single item
var singleItem = items.FirstOrDefault();
if (singleItem is IEnumerable && !(singleItem is string))
{
dragInfo.Data = items;
}
else
{
dragInfo.Data = singleItem;
}
}
dragInfo.Effects = dragInfo.Data != null ? DragDropEffects.Copy | DragDropEffects.Move : DragDropEffects.None;
}
/// <summary>
/// Determines whether this instance [can start drag] the specified drag information.
/// </summary>
/// <param name="dragInfo">The drag information.</param>
/// <returns></returns>
public virtual bool CanStartDrag(IDragInfo dragInfo)
{
return true;
}
/// <summary>
/// Notifies the drag handler that a drop has occurred.
/// </summary>
/// <param name="dropInfo">Information about the drop.</param>
public virtual void Dropped(IDropInfo dropInfo)
{
}
/// <summary>
/// Notifies the drag handler that a drag and drop operation has finished.
/// </summary>
/// <param name="operationResult">The operation result.</param>
/// <param name="dragInfo">The drag information.</param>
public virtual void DragDropOperationFinished(DragDropEffects operationResult, IDragInfo dragInfo)
{
// nothing here
}
/// <summary>
/// Notifies the drag handler that a drag has been aborted.
/// </summary>
public virtual void DragCancelled()
{
}
/// <summary>
/// Notifies that an exception has occurred upon dragging.
/// </summary>
/// <param name="exception">The exception that occurrred.</param>
/// <returns>
/// Boolean indicating whether the exception is handled in the drag handler.
/// False will rethrow the exception.
/// </returns>
public virtual bool TryCatchOccurredException(Exception exception)
{
return false;
}
}
}

View File

@@ -0,0 +1,285 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using GongSolutions.Wpf.DragDrop.Utilities;
using JetBrains.Annotations;
namespace GongSolutions.Wpf.DragDrop
{
/// <summary>
/// A default insertion drop handler for the most common usages
/// </summary>
public class DefaultDropHandler : IDropTarget
{
/// <summary>
/// Test the specified drop information for the right data.
/// </summary>
/// <param name="dropInfo">The drop information.</param>
public static bool CanAcceptData(IDropInfo dropInfo)
{
if (dropInfo == null || dropInfo.DragInfo == null)
{
return false;
}
if (!dropInfo.IsSameDragDropContextAsSource)
{
return false;
}
// do not drop on itself
var isTreeViewItem = dropInfo.InsertPosition.HasFlag(RelativeInsertPosition.TargetItemCenter)
&& dropInfo.VisualTargetItem is TreeViewItem;
if (isTreeViewItem && dropInfo.VisualTargetItem == dropInfo.DragInfo.VisualSourceItem)
{
return false;
}
if (dropInfo.DragInfo.SourceCollection == dropInfo.TargetCollection)
{
var targetList = dropInfo.TargetCollection.TryGetList();
return targetList != null;
}
// else if (dropInfo.DragInfo.SourceCollection is ItemCollection) {
// return false;
// }
else if (dropInfo.TargetCollection == null)
{
return false;
}
else
{
if (TestCompatibleTypes(dropInfo.TargetCollection, dropInfo.Data))
{
var isChildOf = IsChildOf(dropInfo.VisualTargetItem, dropInfo.DragInfo.VisualSourceItem);
return !isChildOf;
}
else
{
return false;
}
}
}
public static IEnumerable ExtractData(object data)
{
if (data is IEnumerable && !(data is string))
{
return (IEnumerable)data;
}
return Enumerable.Repeat(data, 1);
}
/// <summary>
/// Clears the current selected items and selects the given items.
/// </summary>
/// <param name="dropInfo">The drop information.</param>
/// <param name="items">The items which should be select.</param>
/// <param name="applyTemplate">if set to <c>true</c> then for all items the ApplyTemplate will be invoked.</param>
/// <param name="focusVisualTarget">if set to <c>true</c> the visual target will be focused.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="dropInfo" /> is <see langword="null" /></exception>
/// <exception cref="System.ArgumentNullException"><paramref name="dropInfo" /> is <see langword="null" /></exception>
public static void SelectDroppedItems([NotNull] IDropInfo dropInfo, [NotNull] IEnumerable items, bool applyTemplate = true, bool focusVisualTarget = true)
{
if (dropInfo == null) throw new ArgumentNullException(nameof(dropInfo));
if (items == null) throw new ArgumentNullException(nameof(items));
var itemsControl = dropInfo.VisualTarget as ItemsControl;
if (itemsControl != null)
{
var tvItem = dropInfo.VisualTargetItem as TreeViewItem;
var tvItemIsExpanded = tvItem != null && tvItem.HasHeader && tvItem.HasItems && tvItem.IsExpanded;
var itemsParent = tvItemIsExpanded ? tvItem : (dropInfo.VisualTargetItem != null ? ItemsControl.ItemsControlFromItemContainer(dropInfo.VisualTargetItem) : itemsControl);
itemsParent = itemsParent ?? itemsControl;
itemsParent.ClearSelectedItems();
foreach (var obj in items)
{
if (applyTemplate)
{
// call ApplyTemplate for TabItem in TabControl to avoid this error:
//
// System.Windows.Data Error: 4 : Cannot find source for binding with reference
var container = itemsParent.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
container?.ApplyTemplate();
}
itemsParent.SetItemSelected(obj, true);
}
if (focusVisualTarget)
{
itemsControl.Focus();
}
}
}
/// <summary>
/// Determines whether the data of the drag drop action should be copied otherwise moved.
/// </summary>
/// <param name="dropInfo">The DropInfo with a valid DragInfo.</param>
public static bool ShouldCopyData(IDropInfo dropInfo)
{
// default should always the move action/effect
if (dropInfo == null || dropInfo.DragInfo == null)
{
return false;
}
var copyData = ((dropInfo.DragInfo.DragDropCopyKeyState != default(DragDropKeyStates)) && dropInfo.KeyStates.HasFlag(dropInfo.DragInfo.DragDropCopyKeyState))
|| dropInfo.DragInfo.DragDropCopyKeyState.HasFlag(DragDropKeyStates.LeftMouseButton);
copyData = copyData
//&& (dropInfo.DragInfo.VisualSource != dropInfo.VisualTarget)
&& !(dropInfo.DragInfo.SourceItem is HeaderedContentControl)
&& !(dropInfo.DragInfo.SourceItem is HeaderedItemsControl)
&& !(dropInfo.DragInfo.SourceItem is ListBoxItem);
return copyData;
}
/// <summary>
/// Updates the current drag state.
/// </summary>
/// <param name="dropInfo">Information about the drag.</param>
/// <remarks>
/// To allow a drop at the current drag position, the <see cref="DropInfo.Effects" /> property on
/// <paramref name="dropInfo" /> should be set to a value other than <see cref="DragDropEffects.None" />
/// and <see cref="DropInfo.Data" /> should be set to a non-null value.
/// </remarks>
public virtual void DragOver(IDropInfo dropInfo)
{
if (CanAcceptData(dropInfo))
{
dropInfo.Effects = ShouldCopyData(dropInfo) ? DragDropEffects.Copy : DragDropEffects.Move;
var isTreeViewItem = dropInfo.InsertPosition.HasFlag(RelativeInsertPosition.TargetItemCenter) && dropInfo.VisualTargetItem is TreeViewItem;
dropInfo.DropTargetAdorner = isTreeViewItem ? DropTargetAdorners.Highlight : DropTargetAdorners.Insert;
}
}
/// <summary>
/// Performs a drop.
/// </summary>
/// <param name="dropInfo">Information about the drop.</param>
public virtual void Drop(IDropInfo dropInfo)
{
if (dropInfo == null || dropInfo.DragInfo == null)
{
return;
}
var insertIndex = dropInfo.UnfilteredInsertIndex;
var itemsControl = dropInfo.VisualTarget as ItemsControl;
if (itemsControl != null)
{
var editableItems = itemsControl.Items as IEditableCollectionView;
if (editableItems != null)
{
var newItemPlaceholderPosition = editableItems.NewItemPlaceholderPosition;
if (newItemPlaceholderPosition == NewItemPlaceholderPosition.AtBeginning && insertIndex == 0)
{
++insertIndex;
}
else if (newItemPlaceholderPosition == NewItemPlaceholderPosition.AtEnd && insertIndex == itemsControl.Items.Count)
{
--insertIndex;
}
}
}
var destinationList = dropInfo.TargetCollection.TryGetList();
var data = ExtractData(dropInfo.Data).OfType<object>().ToList();
var copyData = ShouldCopyData(dropInfo);
if (!copyData)
{
var sourceList = dropInfo.DragInfo.SourceCollection.TryGetList();
if (sourceList != null)
{
foreach (var o in data)
{
var index = sourceList.IndexOf(o);
if (index != -1)
{
sourceList.RemoveAt(index);
// so, is the source list the destination list too ?
if (destinationList != null && Equals(sourceList, destinationList) && index < insertIndex)
{
--insertIndex;
}
}
}
}
}
if (destinationList != null)
{
var objects2Insert = new List<object>();
// check for cloning
var cloneData = dropInfo.Effects.HasFlag(DragDropEffects.Copy) || dropInfo.Effects.HasFlag(DragDropEffects.Link);
foreach (var o in data)
{
var obj2Insert = o;
if (cloneData)
{
var cloneable = o as ICloneable;
if (cloneable != null)
{
obj2Insert = cloneable.Clone();
}
}
objects2Insert.Add(obj2Insert);
destinationList.Insert(insertIndex++, obj2Insert);
}
var selectDroppedItems = itemsControl is TabControl || (itemsControl != null && DragDrop.GetSelectDroppedItems(itemsControl));
if (selectDroppedItems)
{
SelectDroppedItems(dropInfo, objects2Insert);
}
}
}
protected static bool IsChildOf(UIElement targetItem, UIElement sourceItem)
{
var parent = ItemsControl.ItemsControlFromItemContainer(targetItem);
while (parent != null)
{
if (parent == sourceItem)
{
return true;
}
parent = ItemsControl.ItemsControlFromItemContainer(parent);
}
return false;
}
protected static bool TestCompatibleTypes(IEnumerable target, object data)
{
TypeFilter filter = (t, o) => { return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); };
var enumerableInterfaces = target.GetType().FindInterfaces(filter, null);
var enumerableTypes = from i in enumerableInterfaces
select i.GetGenericArguments().Single();
if (enumerableTypes.Count() > 0)
{
var dataType = TypeUtilities.GetCommonBaseClass(ExtractData(data));
return enumerableTypes.Any(t => t.IsAssignableFrom(dataType));
}
else
{
return target is IList;
}
}
}
}

View File

@@ -0,0 +1,30 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- NuGet -->
<PropertyGroup>
<Authors>Jan Karger, Steven Kirk, mitchell.jon</Authors>
<PackageId>gong-wpf-dragdrop</PackageId>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>https://github.com/punker76/gong-wpf-dragdrop</PackageProjectUrl>
<PackageIconUrl>https://raw.github.com/punker76/gong-wpf-dragdrop/master/GongSolutions.Wpf.DragDrop.png</PackageIconUrl>
<PackageTags>WPF;Windows;UI;XAML;Toolkit;Library;.NET</PackageTags>
<PackageReleaseNotes>https://github.com/punker76/gong-wpf-dragdrop/releases</PackageReleaseNotes>
<RepositoryUrl>https://github.com/punker76/gong-wpf-dragdrop.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<GenerateLibraryLayout>true</GenerateLibraryLayout>
</PropertyGroup>
<!-- Sign assembly -->
<PropertyGroup>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\gong.public.snk</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\LICENSE" Pack="true" PackagePath="" />
</ItemGroup>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))" />
</Project>

View File

@@ -0,0 +1,76 @@
using System.Windows.Documents;
using System.Windows;
using System.Windows.Media;
namespace GongSolutions.Wpf.DragDrop
{
internal class DragAdorner : Adorner
{
public DragAdorner(UIElement adornedElement, UIElement adornment, Point translation, DragDropEffects effects = DragDropEffects.None)
: base(adornedElement)
{
this._translation = translation;
this.m_AdornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
this.m_AdornerLayer.Add(this);
this.m_Adornment = adornment;
this.IsHitTestVisible = false;
this.Effects = effects;
}
public DragDropEffects Effects { get; private set; }
public Point MousePosition
{
get { return this.m_MousePosition; }
set
{
if (this.m_MousePosition != value)
{
this.m_MousePosition = value;
this.m_AdornerLayer.Update(this.AdornedElement);
}
}
}
public void Detatch()
{
this.m_AdornerLayer.Remove(this);
}
protected override Size ArrangeOverride(Size finalSize)
{
this.m_Adornment.Arrange(new Rect(finalSize));
return finalSize;
}
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
{
var result = new GeneralTransformGroup();
result.Children.Add(base.GetDesiredTransform(transform));
result.Children.Add(new TranslateTransform(this.MousePosition.X + this._translation.X, this.MousePosition.Y + this._translation.Y));
return result;
}
protected override Visual GetVisualChild(int index)
{
return this.m_Adornment;
}
protected override Size MeasureOverride(Size constraint)
{
this.m_Adornment.Measure(constraint);
return this.m_Adornment.DesiredSize;
}
protected override int VisualChildrenCount
{
get { return 1; }
}
private readonly AdornerLayer m_AdornerLayer;
private readonly UIElement m_Adornment;
private Point m_MousePosition;
private Point _translation;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,738 @@
using System;
using System.Collections;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using GongSolutions.Wpf.DragDrop.Icons;
using GongSolutions.Wpf.DragDrop.Utilities;
namespace GongSolutions.Wpf.DragDrop
{
public static partial class DragDrop
{
private static void CreateDragAdorner(DropInfo dropInfo)
{
var dragInfo = dropInfo.DragInfo;
var template = GetDropAdornerTemplate(dropInfo.VisualTarget) ?? GetDragAdornerTemplate(dragInfo.VisualSource);
var templateSelector = GetDropAdornerTemplateSelector(dropInfo.VisualTarget) ?? GetDragAdornerTemplateSelector(dragInfo.VisualSource);
UIElement adornment = null;
var useDefaultDragAdorner = template == null && templateSelector == null && GetUseDefaultDragAdorner(dragInfo.VisualSource);
var useVisualSourceItemSizeForDragAdorner = GetUseVisualSourceItemSizeForDragAdorner(dragInfo.VisualSource);
if (useDefaultDragAdorner)
{
template = dragInfo.VisualSourceItem.GetCaptureScreenDataTemplate(dragInfo.VisualSourceFlowDirection);
}
if (template != null || templateSelector != null)
{
if (dragInfo.Data is IEnumerable && !(dragInfo.Data is string))
{
if (!useDefaultDragAdorner && ((IEnumerable)dragInfo.Data).Cast<object>().Count() <= 10)
{
var itemsControl = new ItemsControl();
itemsControl.ItemsSource = (IEnumerable)dragInfo.Data;
itemsControl.ItemTemplate = template;
itemsControl.ItemTemplateSelector = templateSelector;
itemsControl.Tag = dragInfo;
if (useVisualSourceItemSizeForDragAdorner)
{
var bounds = VisualTreeHelper.GetDescendantBounds(dragInfo.VisualSourceItem);
itemsControl.SetValue(FrameworkElement.MinWidthProperty, bounds.Width);
}
// The ItemsControl doesn't display unless we create a grid to contain it.
// Not quite sure why we need this...
var grid = new Grid();
grid.Children.Add(itemsControl);
adornment = grid;
}
}
else
{
var contentPresenter = new ContentPresenter();
contentPresenter.Content = dragInfo.Data;
contentPresenter.ContentTemplate = template;
contentPresenter.ContentTemplateSelector = templateSelector;
contentPresenter.Tag = dragInfo;
if (useVisualSourceItemSizeForDragAdorner)
{
var bounds = VisualTreeHelper.GetDescendantBounds(dragInfo.VisualSourceItem);
contentPresenter.SetValue(FrameworkElement.MinWidthProperty, bounds.Width);
contentPresenter.SetValue(FrameworkElement.MinHeightProperty, bounds.Height);
}
adornment = contentPresenter;
}
}
if (adornment != null)
{
if (useDefaultDragAdorner)
{
adornment.SetCurrentValue(UIElement.OpacityProperty, GetDefaultDragAdornerOpacity(dragInfo.VisualSource));
}
var rootElement = RootElementFinder.FindRoot(dropInfo.VisualTarget ?? dragInfo.VisualSource);
DragAdorner = new DragAdorner(rootElement, adornment, GetDragAdornerTranslation(dragInfo.VisualSource));
}
}
private static void CreateEffectAdorner(DropInfo dropInfo)
{
var dragInfo = m_DragInfo;
var template = GetEffectAdornerTemplate(dragInfo.VisualSource, dropInfo.Effects, dropInfo.DestinationText, dropInfo.EffectText);
if (template != null)
{
var rootElement = RootElementFinder.FindRoot(dropInfo.VisualTarget ?? dragInfo.VisualSource);
var adornment = new ContentPresenter();
adornment.Content = dragInfo.Data;
adornment.ContentTemplate = template;
EffectAdorner = new DragAdorner(rootElement, adornment, GetEffectAdornerTranslation(dragInfo.VisualSource), dropInfo.Effects);
}
}
private static DataTemplate GetEffectAdornerTemplate(UIElement target, DragDropEffects effect, string destinationText, string effectText = null)
{
switch (effect)
{
case DragDropEffects.All:
// TODO: Add default template for EffectAll
return GetEffectAllAdornerTemplate(target);
case DragDropEffects.Copy:
return GetEffectCopyAdornerTemplate(target) ?? CreateDefaultEffectDataTemplate(target, IconFactory.EffectCopy, effectText == null ? "Copy to" : effectText, destinationText);
case DragDropEffects.Link:
return GetEffectLinkAdornerTemplate(target) ?? CreateDefaultEffectDataTemplate(target, IconFactory.EffectLink, effectText == null ? "Link to" : effectText, destinationText);
case DragDropEffects.Move:
return GetEffectMoveAdornerTemplate(target) ?? CreateDefaultEffectDataTemplate(target, IconFactory.EffectMove, effectText == null ? "Move to" : effectText, destinationText);
case DragDropEffects.None:
return GetEffectNoneAdornerTemplate(target) ?? CreateDefaultEffectDataTemplate(target, IconFactory.EffectNone, effectText == null ? "None" : effectText, destinationText);
case DragDropEffects.Scroll:
// TODO: Add default template EffectScroll
return GetEffectScrollAdornerTemplate(target);
default:
return null;
}
}
private static DataTemplate CreateDefaultEffectDataTemplate(UIElement target, BitmapImage effectIcon, string effectText, string destinationText)
{
if (!GetUseDefaultEffectDataTemplate(target))
{
return null;
}
var fontSize = SystemFonts.MessageFontSize; // before 11d
// The icon
var imageFactory = new FrameworkElementFactory(typeof(Image));
imageFactory.SetValue(Image.SourceProperty, effectIcon);
imageFactory.SetValue(FrameworkElement.HeightProperty, 12d);
imageFactory.SetValue(FrameworkElement.WidthProperty, 12d);
// Only the icon for no effect
if (Equals(effectIcon, GongSolutions.Wpf.DragDrop.Icons.IconFactory.EffectNone))
{
return new DataTemplate { VisualTree = imageFactory };
}
// Some margin for the icon
imageFactory.SetValue(FrameworkElement.MarginProperty, new Thickness(0, 0, 3, 0));
imageFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
// Add effect text
var effectTextBlockFactory = new FrameworkElementFactory(typeof(TextBlock));
effectTextBlockFactory.SetValue(TextBlock.TextProperty, effectText);
effectTextBlockFactory.SetValue(TextBlock.FontSizeProperty, fontSize);
effectTextBlockFactory.SetValue(TextBlock.ForegroundProperty, Brushes.Blue);
effectTextBlockFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
// Add destination text
var destinationTextBlockFactory = new FrameworkElementFactory(typeof(TextBlock));
destinationTextBlockFactory.SetValue(TextBlock.TextProperty, destinationText);
destinationTextBlockFactory.SetValue(TextBlock.FontSizeProperty, fontSize);
destinationTextBlockFactory.SetValue(TextBlock.ForegroundProperty, Brushes.DarkBlue);
destinationTextBlockFactory.SetValue(TextBlock.MarginProperty, new Thickness(3, 0, 0, 0));
destinationTextBlockFactory.SetValue(TextBlock.FontWeightProperty, FontWeights.DemiBold);
destinationTextBlockFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
// Create containing panel
var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
stackPanelFactory.SetValue(FrameworkElement.MarginProperty, new Thickness(2));
stackPanelFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
stackPanelFactory.AppendChild(imageFactory);
stackPanelFactory.AppendChild(effectTextBlockFactory);
stackPanelFactory.AppendChild(destinationTextBlockFactory);
// Add border
var borderFactory = new FrameworkElementFactory(typeof(Border));
var stopCollection = new GradientStopCollection
{
new GradientStop(Colors.White, 0.0),
new GradientStop(Colors.AliceBlue, 1.0)
};
var gradientBrush = new LinearGradientBrush(stopCollection)
{
StartPoint = new Point(0, 0),
EndPoint = new Point(0, 1)
};
borderFactory.SetValue(Panel.BackgroundProperty, gradientBrush);
borderFactory.SetValue(Border.BorderBrushProperty, Brushes.DimGray);
borderFactory.SetValue(Border.CornerRadiusProperty, new CornerRadius(3));
borderFactory.SetValue(Border.BorderThicknessProperty, new Thickness(1));
borderFactory.SetValue(Border.SnapsToDevicePixelsProperty, true);
borderFactory.SetValue(TextOptions.TextFormattingModeProperty, TextFormattingMode.Display);
borderFactory.SetValue(TextOptions.TextRenderingModeProperty, TextRenderingMode.ClearType);
borderFactory.SetValue(TextOptions.TextHintingModeProperty, TextHintingMode.Fixed);
borderFactory.AppendChild(stackPanelFactory);
// Finally add content to template
return new DataTemplate { VisualTree = borderFactory };
}
private static void Scroll(DropInfo dropInfo, DragEventArgs e)
{
if (dropInfo == null || dropInfo.TargetScrollViewer == null)
{
return;
}
var scrollViewer = dropInfo.TargetScrollViewer;
var scrollingMode = dropInfo.TargetScrollingMode;
var position = e.GetPosition(scrollViewer);
var scrollMargin = Math.Min(scrollViewer.FontSize * 2, scrollViewer.ActualHeight / 2);
if (scrollingMode == ScrollingMode.Both || scrollingMode == ScrollingMode.HorizontalOnly)
{
if (position.X >= scrollViewer.ActualWidth - scrollMargin && scrollViewer.HorizontalOffset < scrollViewer.ExtentWidth - scrollViewer.ViewportWidth)
{
scrollViewer.LineRight();
}
else if (position.X < scrollMargin && scrollViewer.HorizontalOffset > 0)
{
scrollViewer.LineLeft();
}
}
if (scrollingMode == ScrollingMode.Both || scrollingMode == ScrollingMode.VerticalOnly)
{
if (position.Y >= scrollViewer.ActualHeight - scrollMargin && scrollViewer.VerticalOffset < scrollViewer.ExtentHeight - scrollViewer.ViewportHeight)
{
scrollViewer.LineDown();
}
else if (position.Y < scrollMargin && scrollViewer.VerticalOffset > 0)
{
scrollViewer.LineUp();
}
}
}
/// <summary>
/// Gets the drag handler from the drag info or from the sender, if the drag info is null
/// </summary>
/// <param name="dragInfo">the drag info object</param>
/// <param name="sender">the sender from an event, e.g. mouse down, mouse move</param>
/// <returns></returns>
private static IDragSource TryGetDragHandler(DragInfo dragInfo, UIElement sender)
{
IDragSource dragHandler = null;
if (dragInfo != null && dragInfo.VisualSource != null)
{
dragHandler = GetDragHandler(dragInfo.VisualSource);
}
if (dragHandler == null && sender != null)
{
dragHandler = GetDragHandler(sender);
}
return dragHandler ?? DefaultDragHandler;
}
/// <summary>
/// Gets the drop handler from the drop info or from the sender, if the drop info is null
/// </summary>
/// <param name="dropInfo">the drop info object</param>
/// <param name="sender">the sender from an event, e.g. drag over</param>
/// <returns></returns>
private static IDropTarget TryGetDropHandler(DropInfo dropInfo, UIElement sender)
{
IDropTarget dropHandler = null;
if (dropInfo != null && dropInfo.VisualTarget != null)
{
dropHandler = GetDropHandler(dropInfo.VisualTarget);
}
if (dropHandler == null && sender != null)
{
dropHandler = GetDropHandler(sender);
}
return dropHandler ?? DefaultDropHandler;
}
private static void DragSourceOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DoMouseButtonDown(sender, e);
}
private static void DragSourceOnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
DoMouseButtonDown(sender, e);
}
private static void DoMouseButtonDown(object sender, MouseButtonEventArgs e)
{
m_DragInfo = null;
// Ignore the click if clickCount != 1 or the user has clicked on a scrollbar.
var elementPosition = e.GetPosition((IInputElement)sender);
if (e.ClickCount != 1
|| (sender as UIElement).IsDragSourceIgnored()
|| (e.Source as UIElement).IsDragSourceIgnored()
|| (e.OriginalSource as UIElement).IsDragSourceIgnored()
|| (sender is TabControl) && !HitTestUtilities.HitTest4Type<TabPanel>(sender, elementPosition)
|| HitTestUtilities.HitTest4Type<RangeBase>(sender, elementPosition)
|| HitTestUtilities.HitTest4Type<TextBoxBase>(sender, elementPosition)
|| HitTestUtilities.HitTest4Type<PasswordBox>(sender, elementPosition)
|| HitTestUtilities.HitTest4Type<ComboBox>(sender, elementPosition)
|| HitTestUtilities.HitTest4GridViewColumnHeader(sender, elementPosition)
|| HitTestUtilities.HitTest4DataGridTypes(sender, elementPosition)
|| HitTestUtilities.IsNotPartOfSender(sender, e))
{
return;
}
var dragInfo = new DragInfo(sender, e);
if (dragInfo.VisualSource is ItemsControl control && control.CanSelectMultipleItems())
{
control.Focus();
}
if (dragInfo.VisualSourceItem == null)
{
return;
}
var dragHandler = TryGetDragHandler(dragInfo, sender as UIElement);
if (!dragHandler.CanStartDrag(dragInfo))
{
return;
}
// If the sender is a list box that allows multiple selections, ensure that clicking on an
// already selected item does not change the selection, otherwise dragging multiple items
// is made impossible.
var itemsControl = sender as ItemsControl;
if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0 && (Keyboard.Modifiers & ModifierKeys.Control) == 0 && dragInfo.VisualSourceItem != null && itemsControl != null && itemsControl.CanSelectMultipleItems())
{
var selectedItems = itemsControl.GetSelectedItems().OfType<object>().ToList();
if (selectedItems.Count > 1 && selectedItems.Contains(dragInfo.SourceItem))
{
m_ClickSupressItem = dragInfo.SourceItem;
e.Handled = true;
}
}
m_DragInfo = dragInfo;
}
private static void DragSourceOnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
DoMouseButtonUp(sender, e);
}
private static void DragSourceOnMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
DoMouseButtonUp(sender, e);
}
private static void DoMouseButtonUp(object sender, MouseButtonEventArgs e)
{
var elementPosition = e.GetPosition((IInputElement)sender);
if ((sender is TabControl) && !HitTestUtilities.HitTest4Type<TabPanel>(sender, elementPosition))
{
m_DragInfo = null;
m_ClickSupressItem = null;
return;
}
var dragInfo = m_DragInfo;
// If we prevented the control's default selection handling in DragSource_PreviewMouseLeftButtonDown
// by setting 'e.Handled = true' and a drag was not initiated, manually set the selection here.
var itemsControl = sender as ItemsControl;
if (itemsControl != null && dragInfo != null && m_ClickSupressItem != null && m_ClickSupressItem == dragInfo.SourceItem)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) != 0)
{
itemsControl.SetItemSelected(dragInfo.SourceItem, false);
}
else if ((Keyboard.Modifiers & ModifierKeys.Shift) == 0)
{
itemsControl.SetSelectedItem(dragInfo.SourceItem);
}
}
m_DragInfo = null;
m_ClickSupressItem = null;
}
private static void DragSourceOnMouseMove(object sender, MouseEventArgs e)
{
var dragInfo = m_DragInfo;
if (dragInfo != null && !m_DragInProgress)
{
// the start from the source
var dragStart = dragInfo.DragStartPosition;
// do nothing if mouse left/right button is released or the pointer is captured
if (dragInfo.MouseButton == MouseButton.Left && e.LeftButton == MouseButtonState.Released)
{
m_DragInfo = null;
return;
}
if (DragDrop.GetCanDragWithMouseRightButton(dragInfo.VisualSource) && dragInfo.MouseButton == MouseButton.Right && e.RightButton == MouseButtonState.Released)
{
m_DragInfo = null;
return;
}
// current mouse position
var position = e.GetPosition((IInputElement)sender);
// prevent selection changing while drag operation
dragInfo.VisualSource?.ReleaseMouseCapture();
// only if the sender is the source control and the mouse point differs from an offset
if (dragInfo.VisualSource == sender
&& (Math.Abs(position.X - dragStart.X) > DragDrop.GetMinimumHorizontalDragDistance(dragInfo.VisualSource) ||
Math.Abs(position.Y - dragStart.Y) > DragDrop.GetMinimumVerticalDragDistance(dragInfo.VisualSource)))
{
dragInfo.RefreshSelectedItems(sender, e);
var dragHandler = TryGetDragHandler(dragInfo, sender as UIElement);
if (dragHandler.CanStartDrag(dragInfo))
{
dragHandler.StartDrag(dragInfo);
if (dragInfo.Effects != DragDropEffects.None)
{
var dataObject = dragInfo.DataObject;
if (dataObject == null)
{
if (dragInfo.Data == null)
{
// it's bad if the Data is null, cause the DataObject constructor will raise an ArgumentNullException
m_DragInfo = null; // maybe not necessary or should not set here to null
return;
}
dataObject = new DataObject(dragInfo.DataFormat.Name, dragInfo.Data);
}
try
{
m_DragInProgress = true;
var dragDropHandler = dragInfo.DragDropHandler ?? System.Windows.DragDrop.DoDragDrop;
var dragDropEffects = dragDropHandler(dragInfo.VisualSource, dataObject, dragInfo.Effects);
if (dragDropEffects == DragDropEffects.None)
{
dragHandler.DragCancelled();
}
dragHandler.DragDropOperationFinished(dragDropEffects, dragInfo);
}
catch (Exception ex)
{
if (!dragHandler.TryCatchOccurredException(ex))
{
throw;
}
}
finally
{
m_DragInProgress = false;
m_DragInfo = null;
}
}
}
}
}
}
private static void DragSourceOnQueryContinueDrag(object sender, QueryContinueDragEventArgs e)
{
if (e.Action == DragAction.Cancel || e.EscapePressed)
{
DragAdorner = null;
EffectAdorner = null;
DropTargetAdorner = null;
Mouse.OverrideCursor = null;
}
}
private static void DropTargetOnDragEnter(object sender, DragEventArgs e)
{
DropTargetOnDragOver(sender, e, EventType.Bubbled);
}
private static void DropTargetOnPreviewDragEnter(object sender, DragEventArgs e)
{
DropTargetOnDragOver(sender, e, EventType.Tunneled);
}
private static void DropTargetOnDragLeave(object sender, DragEventArgs e)
{
DragAdorner = null;
EffectAdorner = null;
DropTargetAdorner = null;
}
private static void DropTargetOnDragOver(object sender, DragEventArgs e)
{
DropTargetOnDragOver(sender, e, EventType.Bubbled);
}
private static void DropTargetOnPreviewDragOver(object sender, DragEventArgs e)
{
DropTargetOnDragOver(sender, e, EventType.Tunneled);
}
private static void DropTargetOnDragOver(object sender, DragEventArgs e, EventType eventType)
{
var elementPosition = e.GetPosition((IInputElement)sender);
var dragInfo = m_DragInfo;
var dropInfo = new DropInfo(sender, e, dragInfo, eventType);
var dropHandler = TryGetDropHandler(dropInfo, sender as UIElement);
var itemsControl = dropInfo.VisualTarget;
dropHandler.DragOver(dropInfo);
if (DragAdorner == null && dragInfo != null)
{
CreateDragAdorner(dropInfo);
}
if (DragAdorner != null)
{
var tempAdornerPos = e.GetPosition(DragAdorner.AdornedElement);
if (tempAdornerPos.X >= 0 && tempAdornerPos.Y >= 0)
{
_adornerPos = tempAdornerPos;
}
// Fixed the flickering adorner - Size changes to zero 'randomly'...?
if (DragAdorner.RenderSize.Width > 0 && DragAdorner.RenderSize.Height > 0)
{
_adornerSize = DragAdorner.RenderSize;
}
if (dragInfo != null)
{
// move the adorner
var offsetX = _adornerSize.Width * -GetDragMouseAnchorPoint(dragInfo.VisualSource).X;
var offsetY = _adornerSize.Height * -GetDragMouseAnchorPoint(dragInfo.VisualSource).Y;
_adornerPos.Offset(offsetX, offsetY);
var maxAdornerPosX = DragAdorner.AdornedElement.RenderSize.Width;
var adornerPosRightX = (_adornerPos.X + _adornerSize.Width);
if (adornerPosRightX > maxAdornerPosX)
{
_adornerPos.Offset(-adornerPosRightX + maxAdornerPosX, 0);
}
if (_adornerPos.Y < 0)
{
_adornerPos.Y = 0;
}
}
DragAdorner.MousePosition = _adornerPos;
DragAdorner.InvalidateVisual();
}
Scroll(dropInfo, e);
if (HitTestUtilities.HitTest4Type<ScrollBar>(sender, elementPosition)
|| HitTestUtilities.HitTest4GridViewColumnHeader(sender, elementPosition)
|| HitTestUtilities.HitTest4DataGridTypesOnDragOver(sender, elementPosition))
{
e.Effects = DragDropEffects.None;
e.Handled = true;
return;
}
// If the target is an ItemsControl then update the drop target adorner.
if (itemsControl != null)
{
// Display the adorner in the control's ItemsPresenter. If there is no
// ItemsPresenter provided by the style, try getting hold of a
// ScrollContentPresenter and using that.
UIElement adornedElement = null;
if (itemsControl is TabControl)
{
adornedElement = itemsControl.GetVisualDescendent<TabPanel>();
}
else if (itemsControl is DataGrid || (itemsControl as ListView)?.View is GridView)
{
adornedElement = itemsControl.GetVisualDescendent<ScrollContentPresenter>() as UIElement ?? itemsControl.GetVisualDescendent<ItemsPresenter>() as UIElement ?? itemsControl;
}
else
{
adornedElement = itemsControl.GetVisualDescendent<ItemsPresenter>() as UIElement ?? itemsControl.GetVisualDescendent<ScrollContentPresenter>() as UIElement ?? itemsControl;
}
if (adornedElement != null)
{
if (dropInfo.DropTargetAdorner == null)
{
DropTargetAdorner = null;
}
else if (!dropInfo.DropTargetAdorner.IsInstanceOfType(DropTargetAdorner))
{
DropTargetAdorner = DropTargetAdorner.Create(dropInfo.DropTargetAdorner, adornedElement, dropInfo);
}
var adorner = DropTargetAdorner;
if (adorner != null)
{
var adornerBrush = GetDropTargetAdornerBrush(dropInfo.VisualTarget);
if (adornerBrush != null)
{
adorner.Pen.SetCurrentValue(Pen.BrushProperty, adornerBrush);
}
adorner.DropInfo = dropInfo;
adorner.InvalidateVisual();
}
}
}
// Set the drag effect adorner if there is one
if (dragInfo != null && (EffectAdorner == null || EffectAdorner.Effects != dropInfo.Effects))
{
CreateEffectAdorner(dropInfo);
}
if (EffectAdorner != null)
{
var adornerPos = e.GetPosition(EffectAdorner.AdornedElement);
//adornerPos.Offset(20, 20);
EffectAdorner.MousePosition = adornerPos;
EffectAdorner.InvalidateVisual();
}
e.Effects = dropInfo.Effects;
e.Handled = !dropInfo.NotHandled;
if (!dropInfo.IsSameDragDropContextAsSource)
{
e.Effects = DragDropEffects.None;
}
}
private static void DropTargetOnDrop(object sender, DragEventArgs e)
{
DropTargetOnDrop(sender, e, EventType.Bubbled);
}
private static void DropTargetOnPreviewDrop(object sender, DragEventArgs e)
{
DropTargetOnDrop(sender, e, EventType.Tunneled);
}
private static void DropTargetOnDrop(object sender, DragEventArgs e, EventType eventType)
{
var dragInfo = m_DragInfo;
var dropInfo = new DropInfo(sender, e, dragInfo, eventType);
var dropHandler = TryGetDropHandler(dropInfo, sender as UIElement);
var dragHandler = TryGetDragHandler(dragInfo, sender as UIElement);
DragAdorner = null;
EffectAdorner = null;
DropTargetAdorner = null;
dropHandler.DragOver(dropInfo);
dropHandler.Drop(dropInfo);
dragHandler.Dropped(dropInfo);
e.Effects = dropInfo.Effects;
e.Handled = !dropInfo.NotHandled;
Mouse.OverrideCursor = null;
}
private static void DropTargetOnGiveFeedback(object sender, GiveFeedbackEventArgs e)
{
if (EffectAdorner != null)
{
e.UseDefaultCursors = false;
e.Handled = true;
if (Mouse.OverrideCursor != Cursors.Arrow)
{
Mouse.OverrideCursor = Cursors.Arrow;
}
}
else
{
e.UseDefaultCursors = true;
e.Handled = true;
if (Mouse.OverrideCursor != null)
{
Mouse.OverrideCursor = null;
}
}
}
private static DragAdorner _DragAdorner;
private static DragAdorner DragAdorner
{
get { return _DragAdorner; }
set
{
_DragAdorner?.Detatch();
_DragAdorner = value;
}
}
private static DragAdorner _EffectAdorner;
private static DragAdorner EffectAdorner
{
get { return _EffectAdorner; }
set
{
_EffectAdorner?.Detatch();
_EffectAdorner = value;
}
}
private static DropTargetAdorner _DropTargetAdorner;
private static DropTargetAdorner DropTargetAdorner
{
get { return _DropTargetAdorner; }
set
{
_DropTargetAdorner?.Detatch();
_DropTargetAdorner = value;
}
}
private static DragInfo m_DragInfo;
private static bool m_DragInProgress;
private static object m_ClickSupressItem;
private static Point _adornerPos;
private static Size _adornerSize;
}
}

View File

@@ -0,0 +1,286 @@
using System;
using System.Collections;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using GongSolutions.Wpf.DragDrop.Utilities;
namespace GongSolutions.Wpf.DragDrop
{
/// <summary>
/// Holds information about a the source of a drag drop operation.
/// </summary>
///
/// <remarks>
/// The <see cref="DragInfo"/> class holds all of the framework's information about the source
/// of a drag. It is used by <see cref="IDragSource.StartDrag"/> to determine whether a drag
/// can start, and what the dragged data should be.
/// </remarks>
public class DragInfo : IDragInfo
{
/// <summary>
/// Initializes a new instance of the DragInfo class.
/// </summary>
///
/// <param name="sender">
/// The sender of the mouse event that initiated the drag.
/// </param>
///
/// <param name="e">
/// The mouse event that initiated the drag.
/// </param>
public DragInfo(object sender, MouseButtonEventArgs e)
{
this.Effects = DragDropEffects.None;
this.MouseButton = e.ChangedButton;
this.VisualSource = sender as UIElement;
this.DragStartPosition = e.GetPosition(this.VisualSource);
this.DragDropCopyKeyState = DragDrop.GetDragDropCopyKeyState(this.VisualSource);
var dataFormat = DragDrop.GetDataFormat(this.VisualSource);
if (dataFormat != null)
{
this.DataFormat = dataFormat;
}
var sourceElement = e.OriginalSource as UIElement;
// If we can't cast object as a UIElement it might be a FrameworkContentElement, if so try and use its parent.
if (sourceElement == null && e.OriginalSource is FrameworkContentElement)
{
sourceElement = ((FrameworkContentElement)e.OriginalSource).Parent as UIElement;
}
if (sender is ItemsControl)
{
var itemsControl = (ItemsControl)sender;
this.SourceGroup = itemsControl.FindGroup(this.DragStartPosition);
this.VisualSourceFlowDirection = itemsControl.GetItemsPanelFlowDirection();
UIElement item = null;
if (sourceElement != null)
{
item = itemsControl.GetItemContainer(sourceElement);
}
if (item == null)
{
if (DragDrop.GetDragDirectlySelectedOnly(this.VisualSource))
{
item = itemsControl.GetItemContainerAt(e.GetPosition(itemsControl));
}
else
{
item = itemsControl.GetItemContainerAt(e.GetPosition(itemsControl), itemsControl.GetItemsPanelOrientation());
}
}
if (item != null)
{
// Remember the relative position of the item being dragged
this.PositionInDraggedItem = e.GetPosition(item);
var itemParent = ItemsControl.ItemsControlFromItemContainer(item);
if (itemParent != null)
{
this.SourceCollection = itemParent.ItemsSource ?? itemParent.Items;
if (itemParent != itemsControl)
{
var tvItem = item as TreeViewItem;
if (tvItem != null)
{
var tv = tvItem.GetVisualAncestor<TreeView>();
if (tv != null && tv != itemsControl && !tv.IsDragSource())
{
return;
}
}
else if (itemsControl.ItemContainerGenerator.IndexFromContainer(itemParent) < 0 && !itemParent.IsDragSource())
{
return;
}
}
this.SourceIndex = itemParent.ItemContainerGenerator.IndexFromContainer(item);
this.SourceItem = itemParent.ItemContainerGenerator.ItemFromContainer(item);
}
else
{
this.SourceIndex = -1;
}
var selectedItems = itemsControl.GetSelectedItems().OfType<object>().Where(i => i != CollectionView.NewItemPlaceholder).ToList();
this.SourceItems = selectedItems;
// Some controls (I'm looking at you TreeView!) haven't updated their
// SelectedItem by this point. Check to see if there 1 or less item in
// the SourceItems collection, and if so, override the control's SelectedItems with the clicked item.
//
// The control has still the old selected items at the mouse down event, so we should check this and give only the real selected item to the user.
if (selectedItems.Count <= 1 || this.SourceItem != null && !selectedItems.Contains(this.SourceItem))
{
this.SourceItems = Enumerable.Repeat(this.SourceItem, 1);
}
this.VisualSourceItem = item;
}
else
{
this.SourceCollection = itemsControl.ItemsSource ?? itemsControl.Items;
}
}
else
{
this.SourceItem = (sender as FrameworkElement)?.DataContext;
if (this.SourceItem != null)
{
this.SourceItems = Enumerable.Repeat(this.SourceItem, 1);
}
this.VisualSourceItem = sourceElement;
this.PositionInDraggedItem = sourceElement != null ? e.GetPosition(sourceElement) : this.DragStartPosition;
}
if (this.SourceItems == null)
{
this.SourceItems = Enumerable.Empty<object>();
}
}
internal void RefreshSelectedItems(object sender, MouseEventArgs e)
{
if (sender is ItemsControl)
{
var itemsControl = (ItemsControl)sender;
var selectedItems = itemsControl.GetSelectedItems().OfType<object>().Where(i => i != CollectionView.NewItemPlaceholder).ToList();
this.SourceItems = selectedItems;
// Some controls (I'm looking at you TreeView!) haven't updated their
// SelectedItem by this point. Check to see if there 1 or less item in
// the SourceItems collection, and if so, override the control's SelectedItems with the clicked item.
//
// The control has still the old selected items at the mouse down event, so we should check this and give only the real selected item to the user.
if (selectedItems.Count <= 1 || this.SourceItem != null && !selectedItems.Contains(this.SourceItem))
{
this.SourceItems = Enumerable.Repeat(this.SourceItem, 1);
}
}
}
/// <summary>
/// Gets or sets the data format which will be used for the drag and drop actions.
/// </summary>
/// <value>The data format.</value>
public DataFormat DataFormat { get; set; } = DragDrop.DataFormat;
/// <summary>
/// Gets or sets the drag data.
/// </summary>
///
/// <remarks>
/// This must be set by a drag handler in order for a drag to start.
/// </remarks>
public object Data { get; set; }
/// <summary>
/// Gets the position of the click that initiated the drag, relative to <see cref="VisualSource"/>.
/// </summary>
public Point DragStartPosition { get; private set; }
/// <summary>
/// Gets the point where the cursor was relative to the item being dragged when the drag was started.
/// </summary>
public Point PositionInDraggedItem { get; private set; }
/// <summary>
/// Gets or sets the allowed effects for the drag.
/// </summary>
///
/// <remarks>
/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drag handler in order
/// for a drag to start.
/// </remarks>
public DragDropEffects Effects { get; set; }
/// <summary>
/// Gets the mouse button that initiated the drag.
/// </summary>
public MouseButton MouseButton { get; private set; }
/// <summary>
/// Gets the collection that the source ItemsControl is bound to.
/// </summary>
///
/// <remarks>
/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
/// </remarks>
public IEnumerable SourceCollection { get; private set; }
/// <summary>
/// Gets the position from where the item was dragged.
/// </summary>
/// <value>The index of the source.</value>
public int SourceIndex { get; private set; }
/// <summary>
/// Gets the object that a dragged item is bound to.
/// </summary>
///
/// <remarks>
/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
/// </remarks>
public object SourceItem { get; private set; }
/// <summary>
/// Gets a collection of objects that the selected items in an ItemsControl are bound to.
/// </summary>
///
/// <remarks>
/// If the control that initated the drag is unbound or not an ItemsControl, this will be empty.
/// </remarks>
public IEnumerable SourceItems { get; private set; }
/// <summary>
/// Gets the group from a dragged item if the drag is currently from an ItemsControl with groups.
/// </summary>
public CollectionViewGroup SourceGroup { get; private set; }
/// <summary>
/// Gets the control that initiated the drag.
/// </summary>
public UIElement VisualSource { get; private set; }
/// <summary>
/// Gets the item in an ItemsControl that started the drag.
/// </summary>
///
/// <remarks>
/// If the control that initiated the drag is an ItemsControl, this property will hold the item
/// container of the clicked item. For example, if <see cref="VisualSource"/> is a ListBox this
/// will hold a ListBoxItem.
/// </remarks>
public UIElement VisualSourceItem { get; private set; }
/// <summary>
/// Gets the FlowDirection of the current drag source.
/// </summary>
public FlowDirection VisualSourceFlowDirection { get; private set; }
/// <summary>
/// Gets the <see cref="IDataObject"/> which is used by the drag and drop operation. Set it to
/// a custom instance if custom drag and drop behavior is needed.
/// </summary>
public object DataObject { get; set; }
/// <summary>Initiates a drag-and-drop operation.</summary>
/// <returns>One of the <see cref="T:System.Windows.DragDropEffects" /> values that specifies the final effect that was performed during the drag-and-drop operation.</returns>
public Func<DependencyObject, object, DragDropEffects, DragDropEffects> DragDropHandler { get; set; } = System.Windows.DragDrop.DoDragDrop;
/// <summary>
/// Gets the drag drop copy key state indicating the effect of the drag drop operation.
/// </summary>
public DragDropKeyStates DragDropCopyKeyState { get; private set; }
}
}

View File

@@ -0,0 +1,452 @@
using System;
using System.Collections;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using GongSolutions.Wpf.DragDrop.Utilities;
using JetBrains.Annotations;
namespace GongSolutions.Wpf.DragDrop
{
/// <summary>
/// Holds information about a the target of a drag drop operation.
/// </summary>
///
/// <remarks>
/// The <see cref="DropInfo"/> class holds all of the framework's information about the current
/// target of a drag. It is used by <see cref="IDropTarget.DragOver"/> method to determine whether
/// the current drop target is valid, and by <see cref="IDropTarget.Drop"/> to perform the drop.
/// </remarks>
public class DropInfo : IDropInfo
{
private ItemsControl itemParent = null;
private UIElement item = null;
/// <summary>
/// Initializes a new instance of the DropInfo class.
/// </summary>
///
/// <param name="sender">
/// The sender of the drag event.
/// </param>
///
/// <param name="e">
/// The drag event.
/// </param>
///
/// <param name="dragInfo">
/// Information about the source of the drag, if the drag came from within the framework.
/// </param>
///
/// <param name="eventType">
/// The type of the underlying event (tunneled or bubbled).
/// </param>
public DropInfo(object sender, DragEventArgs e, [CanBeNull] DragInfo dragInfo, EventType eventType)
{
this.DragInfo = dragInfo;
this.KeyStates = e.KeyStates;
this.EventType = eventType;
var dataFormat = dragInfo?.DataFormat;
this.Data = dataFormat != null && e.Data.GetDataPresent(dataFormat.Name) ? e.Data.GetData(dataFormat.Name) : e.Data;
this.VisualTarget = sender as UIElement;
// if there is no drop target, find another
if (!this.VisualTarget.IsDropTarget())
{
// try to find next element
var element = this.VisualTarget.TryGetNextAncestorDropTargetElement();
if (element != null)
{
this.VisualTarget = element;
}
}
// try find ScrollViewer
var dropTargetScrollViewer = DragDrop.GetDropTargetScrollViewer(this.VisualTarget);
if (dropTargetScrollViewer != null)
{
this.TargetScrollViewer = dropTargetScrollViewer;
}
else if (this.VisualTarget is TabControl)
{
var tabPanel = this.VisualTarget.GetVisualDescendent<TabPanel>();
this.TargetScrollViewer = tabPanel?.GetVisualAncestor<ScrollViewer>();
}
else
{
this.TargetScrollViewer = this.VisualTarget?.GetVisualDescendent<ScrollViewer>();
}
this.TargetScrollingMode = this.VisualTarget != null ? DragDrop.GetDropScrollingMode(this.VisualTarget) : ScrollingMode.Both;
// visual target can be null, so give us a point...
this.DropPosition = this.VisualTarget != null ? e.GetPosition(this.VisualTarget) : new Point();
if (this.VisualTarget is TabControl)
{
if (!HitTestUtilities.HitTest4Type<TabPanel>(this.VisualTarget, this.DropPosition))
{
return;
}
}
if (this.VisualTarget is ItemsControl)
{
var itemsControl = (ItemsControl)this.VisualTarget;
//System.Diagnostics.Debug.WriteLine(">>> Name = {0}", itemsControl.Name);
// get item under the mouse
item = itemsControl.GetItemContainerAt(this.DropPosition);
var directlyOverItem = item != null;
this.TargetGroup = itemsControl.FindGroup(this.DropPosition);
this.VisualTargetOrientation = itemsControl.GetItemsPanelOrientation();
this.VisualTargetFlowDirection = itemsControl.GetItemsPanelFlowDirection();
if (item == null)
{
// ok, no item found, so maybe we can found an item at top, left, right or bottom
item = itemsControl.GetItemContainerAt(this.DropPosition, this.VisualTargetOrientation);
directlyOverItem = DropPosition.DirectlyOverElement(this.item, itemsControl);
}
if (item == null && this.TargetGroup != null && this.TargetGroup.IsBottomLevel)
{
var itemData = this.TargetGroup.Items.FirstOrDefault();
if (itemData != null)
{
item = itemsControl.ItemContainerGenerator.ContainerFromItem(itemData) as UIElement;
directlyOverItem = DropPosition.DirectlyOverElement(this.item, itemsControl);
}
}
if (item != null)
{
itemParent = ItemsControl.ItemsControlFromItemContainer(item);
this.VisualTargetOrientation = itemParent.GetItemsPanelOrientation();
this.VisualTargetFlowDirection = itemParent.GetItemsPanelFlowDirection();
this.InsertIndex = itemParent.ItemContainerGenerator.IndexFromContainer(item);
this.TargetCollection = itemParent.ItemsSource ?? itemParent.Items;
var tvItem = item as TreeViewItem;
if (directlyOverItem || tvItem != null)
{
this.VisualTargetItem = item;
this.TargetItem = itemParent.ItemContainerGenerator.ItemFromContainer(item);
}
var expandedTVItem = tvItem != null && tvItem.HasHeader && tvItem.HasItems && tvItem.IsExpanded;
var itemRenderSize = expandedTVItem ? tvItem.GetHeaderSize() : item.RenderSize;
if (this.VisualTargetOrientation == Orientation.Vertical)
{
var currentYPos = e.GetPosition(item).Y;
var targetHeight = itemRenderSize.Height;
var topGap = targetHeight * 0.25;
var bottomGap = targetHeight * 0.75;
if (currentYPos > targetHeight / 2)
{
if (expandedTVItem && (currentYPos < topGap || currentYPos > bottomGap))
{
this.VisualTargetItem = tvItem.ItemContainerGenerator.ContainerFromIndex(0) as UIElement;
this.TargetItem = this.VisualTargetItem != null ? tvItem.ItemContainerGenerator.ItemFromContainer(this.VisualTargetItem) : null;
this.TargetCollection = tvItem.ItemsSource ?? tvItem.Items;
this.InsertIndex = 0;
this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
}
else
{
this.InsertIndex++;
this.InsertPosition = RelativeInsertPosition.AfterTargetItem;
}
}
else
{
this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
}
if (currentYPos > topGap && currentYPos < bottomGap)
{
if (tvItem != null)
{
this.TargetCollection = tvItem.ItemsSource ?? tvItem.Items;
this.InsertIndex = this.TargetCollection != null ? this.TargetCollection.OfType<object>().Count() : 0;
}
this.InsertPosition |= RelativeInsertPosition.TargetItemCenter;
}
//System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, idx={1}, Y={2}, Item={3}", this.InsertPosition, this.InsertIndex, currentYPos, item);
}
else
{
var currentXPos = e.GetPosition(item).X;
var targetWidth = itemRenderSize.Width;
if (this.VisualTargetFlowDirection == FlowDirection.RightToLeft)
{
if (currentXPos > targetWidth / 2)
{
this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
}
else
{
this.InsertIndex++;
this.InsertPosition = RelativeInsertPosition.AfterTargetItem;
}
}
else if (this.VisualTargetFlowDirection == FlowDirection.LeftToRight)
{
if (currentXPos > targetWidth / 2)
{
this.InsertIndex++;
this.InsertPosition = RelativeInsertPosition.AfterTargetItem;
}
else
{
this.InsertPosition = RelativeInsertPosition.BeforeTargetItem;
}
}
if (currentXPos > targetWidth * 0.25 && currentXPos < targetWidth * 0.75)
{
if (tvItem != null)
{
this.TargetCollection = tvItem.ItemsSource ?? tvItem.Items;
this.InsertIndex = this.TargetCollection != null ? this.TargetCollection.OfType<object>().Count() : 0;
}
this.InsertPosition |= RelativeInsertPosition.TargetItemCenter;
}
//System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, idx={1}, X={2}, Item={3}", this.InsertPosition, this.InsertIndex, currentXPos, item);
}
}
else
{
this.TargetCollection = itemsControl.ItemsSource ?? itemsControl.Items;
this.InsertIndex = itemsControl.Items.Count;
//System.Diagnostics.Debug.WriteLine("==> DropInfo: pos={0}, item=NULL, idx={1}", this.InsertPosition, this.InsertIndex);
}
}
else
{
this.VisualTargetItem = this.VisualTarget;
}
}
/// <summary>
/// Gets the drag data.
/// </summary>
///
/// <remarks>
/// If the drag came from within the framework, this will hold:
///
/// - The dragged data if a single item was dragged.
/// - A typed IEnumerable if multiple items were dragged.
/// </remarks>
public object Data { get; private set; }
/// <summary>
/// Gets a <see cref="DragInfo"/> object holding information about the source of the drag,
/// if the drag came from within the framework.
/// </summary>
public IDragInfo DragInfo { get; private set; }
/// <summary>
/// Gets the mouse position relative to the VisualTarget
/// </summary>
public Point DropPosition { get; private set; }
/// <summary>
/// Gets or sets the class of drop target to display.
/// </summary>
///
/// <remarks>
/// The standard drop target adorner classes are held in the <see cref="DropTargetAdorners"/>
/// class.
/// </remarks>
public Type DropTargetAdorner { get; set; }
/// <summary>
/// Gets or sets the allowed effects for the drop.
/// </summary>
///
/// <remarks>
/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drop handler in order
/// for a drop to be possible.
/// </remarks>
public DragDropEffects Effects { get; set; }
/// <summary>
/// Gets the current insert position within <see cref="TargetCollection"/>.
/// </summary>
public int InsertIndex { get; private set; }
/// <summary>
/// Gets the current insert position within the source (unfiltered) <see cref="TargetCollection"/>.
/// </summary>
/// <remarks>
/// This should be only used in a Drop action.
/// This works only correct with different objects (string, int, etc won't work correct).
/// </remarks>
public int UnfilteredInsertIndex
{
get
{
var insertIndex = this.InsertIndex;
if (itemParent != null)
{
var itemSourceAsList = itemParent.ItemsSource.TryGetList();
if (itemSourceAsList != null && itemParent.Items != null && itemParent.Items.Count != itemSourceAsList.Count)
{
if (insertIndex >= 0 && insertIndex < itemParent.Items.Count)
{
var indexOf = itemSourceAsList.IndexOf(itemParent.Items[insertIndex]);
if (indexOf >= 0)
{
return indexOf;
}
}
else if (itemParent.Items.Count > 0 && insertIndex == itemParent.Items.Count)
{
var indexOf = itemSourceAsList.IndexOf(itemParent.Items[insertIndex - 1]);
if (indexOf >= 0)
{
return indexOf + 1;
}
}
}
}
return insertIndex;
}
}
/// <summary>
/// Gets the collection that the target ItemsControl is bound to.
/// </summary>
///
/// <remarks>
/// If the current drop target is unbound or not an ItemsControl, this will be null.
/// </remarks>
public IEnumerable TargetCollection { get; private set; }
/// <summary>
/// Gets the object that the current drop target is bound to.
/// </summary>
///
/// <remarks>
/// If the current drop target is unbound or not an ItemsControl, this will be null.
/// </remarks>
public object TargetItem { get; private set; }
/// <summary>
/// Gets the current group target.
/// </summary>
///
/// <remarks>
/// If the drag is currently over an ItemsControl with groups, describes the group that
/// the drag is currently over.
/// </remarks>
public CollectionViewGroup TargetGroup { get; private set; }
/// <summary>
/// Gets the ScrollViewer control for the visual target.
/// </summary>
public ScrollViewer TargetScrollViewer { get; private set; }
/// <summary>
/// Gets or Sets the ScrollingMode for the drop action.
/// </summary>
public ScrollingMode TargetScrollingMode { get; set; }
/// <summary>
/// Gets the control that is the current drop target.
/// </summary>
public UIElement VisualTarget { get; private set; }
/// <summary>
/// Gets the item in an ItemsControl that is the current drop target.
/// </summary>
///
/// <remarks>
/// If the current drop target is unbound or not an ItemsControl, this will be null.
/// </remarks>
public UIElement VisualTargetItem { get; private set; }
/// <summary>
/// Gets the orientation of the current drop target.
/// </summary>
public Orientation VisualTargetOrientation { get; private set; }
/// <summary>
/// Gets the orientation of the current drop target.
/// </summary>
public FlowDirection VisualTargetFlowDirection { get; private set; }
/// <summary>
/// Gets and sets the text displayed in the DropDropEffects adorner.
/// </summary>
public string DestinationText { get; set; }
/// <summary>
/// Gets and sets the effect text displayed in the DropDropEffects adorner.
/// </summary>
public string EffectText { get; set; }
/// <summary>
/// Gets the relative position the item will be inserted to compared to the TargetItem
/// </summary>
public RelativeInsertPosition InsertPosition { get; private set; }
/// <summary>
/// Gets a flag enumeration indicating the current state of the SHIFT, CTRL, and ALT keys, as well as the state of the mouse buttons.
/// </summary>
public DragDropKeyStates KeyStates { get; private set; }
public bool NotHandled { get; set; }
/// <summary>
/// Gets a value indicating whether the target is in the same context as the source, <see cref="DragDrop.DragDropContextProperty" />.
/// </summary>
public bool IsSameDragDropContextAsSource
{
get
{
// Check if DragInfo stuff exists
if (this.DragInfo == null || this.DragInfo.VisualSource == null)
{
return true;
}
// A target should be exists
if (this.VisualTarget == null)
{
return true;
}
// Source element has a drag context constraint, we need to check the target property matches.
var sourceContext = this.DragInfo.VisualSource.GetValue(DragDrop.DragDropContextProperty) as string;
if (String.IsNullOrEmpty(sourceContext))
{
return true;
}
var targetContext = this.VisualTarget.GetValue(DragDrop.DragDropContextProperty) as string;
return string.Equals(sourceContext, targetContext);
}
}
/// <summary>
/// Gets the current mode of the underlying routed event.
/// </summary>
public EventType EventType { get; }
}
[Flags]
public enum RelativeInsertPosition
{
None = 0,
BeforeTargetItem = 1,
AfterTargetItem = 2,
TargetItemCenter = 4
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace GongSolutions.Wpf.DragDrop
{
public abstract class DropTargetAdorner : Adorner
{
[Obsolete("This constructor is obsolete and will be deleted in next major release.")]
public DropTargetAdorner(UIElement adornedElement)
: this(adornedElement, (DropInfo)null)
{
}
public DropTargetAdorner(UIElement adornedElement, DropInfo dropInfo)
: base(adornedElement)
{
this.DropInfo = dropInfo;
this.IsHitTestVisible = false;
this.AllowDrop = false;
this.SnapsToDevicePixels = true;
this.m_AdornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
this.m_AdornerLayer.Add(this);
}
public DropInfo DropInfo { get; set; }
/// <summary>
/// Gets or Sets the pen which can be used for the render process.
/// </summary>
public Pen Pen { get; set; } = new Pen(Brushes.Gray, 2);
public void Detatch()
{
this.m_AdornerLayer.Remove(this);
}
internal static DropTargetAdorner Create(Type type, UIElement adornedElement, IDropInfo dropInfo)
{
if (!typeof(DropTargetAdorner).IsAssignableFrom(type))
{
throw new InvalidOperationException("The requested adorner class does not derive from DropTargetAdorner.");
}
return type.GetConstructor(new[] { typeof(UIElement), typeof(DropInfo) })?.Invoke(new object[] { adornedElement, dropInfo }) as DropTargetAdorner;
}
private readonly AdornerLayer m_AdornerLayer;
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace GongSolutions.Wpf.DragDrop
{
public class DropTargetAdorners
{
/// <summary>
/// Gets the type of the default highlight target adorner.
/// </summary>
public static Type Highlight { get; } = typeof(DropTargetHighlightAdorner);
/// <summary>
/// Gets the type of the default insert target adorner.
/// </summary>
public static Type Insert { get; } = typeof(DropTargetInsertionAdorner);
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace GongSolutions.Wpf.DragDrop
{
public class DropTargetHighlightAdorner : DropTargetAdorner
{
[Obsolete("This constructor is obsolete and will be deleted in next major release.")]
public DropTargetHighlightAdorner(UIElement adornedElement)
: base(adornedElement, (DropInfo)null)
{
}
public DropTargetHighlightAdorner(UIElement adornedElement, DropInfo dropInfo)
: base(adornedElement, dropInfo)
{
}
/// <summary>
/// When overridden in a derived class, participates in rendering operations that are directed by the layout system.
/// The rendering instructions for this element are not used directly when this method is invoked, and are instead preserved for
/// later asynchronous use by layout and drawing.
/// </summary>
/// <param name="drawingContext">The drawing instructions for a specific element. This context is provided to the layout system.</param>
protected override void OnRender(DrawingContext drawingContext)
{
var dropInfo = this.DropInfo;
var visualTargetItem = dropInfo.VisualTargetItem;
if (visualTargetItem != null)
{
var rect = Rect.Empty;
var tvItem = visualTargetItem as TreeViewItem;
if (tvItem != null && VisualTreeHelper.GetChildrenCount(tvItem) > 0)
{
var descendant = VisualTreeHelper.GetDescendantBounds(tvItem);
var translatePoint = tvItem.TranslatePoint(new Point(), this.AdornedElement);
var itemRect = new Rect(translatePoint, tvItem.RenderSize);
descendant.Union(itemRect);
translatePoint.Offset(1, 0);
rect = new Rect(translatePoint, new Size(descendant.Width - translatePoint.X - 1, tvItem.ActualHeight));
}
if (rect.IsEmpty)
{
rect = new Rect(visualTargetItem.TranslatePoint(new Point(), this.AdornedElement), VisualTreeHelper.GetDescendantBounds(visualTargetItem).Size);
}
drawingContext.DrawRoundedRectangle(null, this.Pen, rect, 2, 2);
}
}
}
}

View File

@@ -0,0 +1,213 @@
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using GongSolutions.Wpf.DragDrop.Utilities;
using System.Windows.Controls.Primitives;
namespace GongSolutions.Wpf.DragDrop
{
public class DropTargetInsertionAdorner : DropTargetAdorner
{
[Obsolete("This constructor is obsolete and will be deleted in next major release.")]
public DropTargetInsertionAdorner(UIElement adornedElement)
: base(adornedElement, (DropInfo)null)
{
}
public DropTargetInsertionAdorner(UIElement adornedElement, DropInfo dropInfo)
: base(adornedElement, dropInfo)
{
}
/// <summary>
/// When overridden in a derived class, participates in rendering operations that are directed by the layout system.
/// The rendering instructions for this element are not used directly when this method is invoked, and are instead preserved for
/// later asynchronous use by layout and drawing.
/// </summary>
/// <param name="drawingContext">The drawing instructions for a specific element. This context is provided to the layout system.</param>
protected override void OnRender(DrawingContext drawingContext)
{
var dropInfo = this.DropInfo;
var itemsControl = dropInfo.VisualTarget as ItemsControl;
if (itemsControl != null)
{
// Get the position of the item at the insertion index. If the insertion point is
// to be after the last item, then get the position of the last item and add an
// offset later to draw it at the end of the list.
ItemsControl itemParent;
var visualTargetItem = dropInfo.VisualTargetItem;
if (visualTargetItem != null)
{
itemParent = ItemsControl.ItemsControlFromItemContainer(visualTargetItem);
}
else
{
itemParent = itemsControl;
}
// this could be happen with a thread scenario where items are removed very quickly
if (itemParent == null)
{
return;
}
var itemsCount = itemParent.Items.Count;
var index = Math.Min(dropInfo.InsertIndex, itemsCount - 1);
var lastItemInGroup = false;
var targetGroup = dropInfo.TargetGroup;
if (targetGroup != null && targetGroup.IsBottomLevel && dropInfo.InsertPosition.HasFlag(RelativeInsertPosition.AfterTargetItem))
{
var indexOf = targetGroup.Items.IndexOf(dropInfo.TargetItem);
lastItemInGroup = indexOf == targetGroup.ItemCount - 1;
if (lastItemInGroup && dropInfo.InsertIndex != itemsCount)
{
index--;
}
}
var itemContainer = (UIElement)itemParent.ItemContainerGenerator.ContainerFromIndex(index);
var showAlwaysDropTargetAdorner = itemContainer == null && DragDrop.GetShowAlwaysDropTargetAdorner(itemParent);
if (showAlwaysDropTargetAdorner)
{
itemContainer = itemParent;
}
if (itemContainer != null)
{
var itemRect = new Rect(itemContainer.TranslatePoint(new Point(), this.AdornedElement), itemContainer.RenderSize);
Point point1,
point2;
double rotation = 0;
// I really don't know why I did this
//
// var viewportWidth = double.MaxValue;
// var viewportHeight = double.MaxValue;
// if (DropInfo.TargetScrollViewer != null)
// {
// if (DropInfo.TargetScrollViewer.ScrollableWidth != 0)
// {
// viewportWidth = DropInfo.TargetScrollViewer.ViewportWidth;
// }
//
// if (DropInfo.TargetScrollViewer.ScrollableHeight != 0)
// {
// viewportHeight = DropInfo.TargetScrollViewer.ViewportHeight;
// }
// }
if (dropInfo.VisualTargetOrientation == Orientation.Vertical)
{
if ((dropInfo.InsertIndex == itemsCount) || lastItemInGroup)
{
if (itemsCount > 0)
{
itemRect.Y += itemContainer.RenderSize.Height;
}
else
{
if ((itemsControl as ListView)?.View is GridView)
{
var header = itemsControl.GetVisualDescendent<GridViewHeaderRowPresenter>();
if (header != null)
{
itemRect.Y += header.RenderSize.Height;
}
}
else if (itemsControl is DataGrid)
{
var header = itemsControl.GetVisualDescendent<DataGridColumnHeadersPresenter>();
if (header != null)
{
itemRect.Y += header.RenderSize.Height;
}
}
itemRect.Y += this.Pen.Thickness;
}
}
var itemRectRight = itemRect.Right; //Math.Min(itemRect.Right, viewportWidth);
var itemRectLeft = itemRect.X < 0 ? 0 : itemRect.X;
point1 = new Point(itemRectLeft, itemRect.Y);
point2 = new Point(itemRectRight, itemRect.Y);
}
else
{
if (dropInfo.VisualTargetFlowDirection == FlowDirection.LeftToRight && dropInfo.InsertIndex == itemsCount)
{
if (itemsCount > 0)
{
itemRect.X += itemContainer.RenderSize.Width;
}
else
{
itemRect.X += this.Pen.Thickness;
}
}
else if (dropInfo.VisualTargetFlowDirection == FlowDirection.RightToLeft && dropInfo.InsertIndex != itemsCount)
{
if (itemsCount > 0)
{
itemRect.X += itemContainer.RenderSize.Width;
}
else
{
itemRect.X += this.Pen.Thickness;
}
}
var itemRectTop = itemRect.Y < 0 ? 0 : itemRect.Y;
var itemRectBottom = itemRect.Bottom; //Math.Min(itemRect.Bottom, viewportHeight);
point1 = new Point(itemRect.X, itemRectTop);
point2 = new Point(itemRect.X, itemRectBottom);
rotation = 90;
}
drawingContext.DrawLine(this.Pen, point1, point2);
this.DrawTriangle(drawingContext, point1, rotation);
this.DrawTriangle(drawingContext, point2, 180 + rotation);
}
}
}
private void DrawTriangle(DrawingContext drawingContext, Point origin, double rotation)
{
drawingContext.PushTransform(new TranslateTransform(origin.X, origin.Y));
drawingContext.PushTransform(new RotateTransform(rotation));
drawingContext.DrawGeometry(this.Pen.Brush, null, m_Triangle);
drawingContext.Pop();
drawingContext.Pop();
}
static DropTargetInsertionAdorner()
{
// Create the pen and triangle in a static constructor and freeze them to improve performance.
const int triangleSize = 5;
var firstLine = new LineSegment(new Point(0, -triangleSize), false);
firstLine.Freeze();
var secondLine = new LineSegment(new Point(0, triangleSize), false);
secondLine.Freeze();
var figure = new PathFigure { StartPoint = new Point(triangleSize, 0) };
figure.Segments.Add(firstLine);
figure.Segments.Add(secondLine);
figure.Freeze();
m_Triangle = new PathGeometry();
m_Triangle.Figures.Add(figure);
m_Triangle.Freeze();
}
private static readonly PathGeometry m_Triangle;
}
}

View File

@@ -0,0 +1,10 @@
namespace GongSolutions.Wpf.DragDrop
{
public enum EventType
{
Auto,
Tunneled,
Bubbled,
TunneledBubbled
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<!-- Project properties -->
<PropertyGroup>
<TargetFrameworks>net45;net46;netcoreapp3.1</TargetFrameworks>
<AssemblyName>GongSolutions.WPF.DragDrop</AssemblyName>
<Title>gong-wpf-dragdrop</Title>
<RootNamespace>GongSolutions.WPF.DragDrop</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<!-- reference includes -->
<ItemGroup>
<PackageReference Include="WpfAnalyzers" Version="2.4.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="JetBrains.Annotations" Version="2020.3.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,124 @@
using System;
using System.Collections;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
namespace GongSolutions.Wpf.DragDrop
{
public interface IDragInfo
{
/// <summary>
/// Gets or sets the data format which will be used for the drag and drop actions.
/// </summary>
/// <value>The data format.</value>
DataFormat DataFormat { get; set; }
/// <summary>
/// Gets or sets the drag data.
/// </summary>
///
/// <remarks>
/// This must be set by a drag handler in order for a drag to start.
/// </remarks>
object Data { get; set; }
/// <summary>
/// Gets the position of the click that initiated the drag, relative to <see cref="VisualSource"/>.
/// </summary>
Point DragStartPosition { get; }
/// <summary>
/// Gets the point where the cursor was relative to the item being dragged when the drag was started.
/// </summary>
Point PositionInDraggedItem { get; }
/// <summary>
/// Gets or sets the allowed effects for the drag.
/// </summary>
///
/// <remarks>
/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drag handler in order
/// for a drag to start.
/// </remarks>
DragDropEffects Effects { get; set; }
/// <summary>
/// Gets the mouse button that initiated the drag.
/// </summary>
MouseButton MouseButton { get; }
/// <summary>
/// Gets the collection that the source ItemsControl is bound to.
/// </summary>
///
/// <remarks>
/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
/// </remarks>
IEnumerable SourceCollection { get; }
/// <summary>
/// Gets the position from where the item was dragged.
/// </summary>
int SourceIndex { get; }
/// <summary>
/// Gets the object that a dragged item is bound to.
/// </summary>
///
/// <remarks>
/// If the control that initated the drag is unbound or not an ItemsControl, this will be null.
/// </remarks>
object SourceItem { get; }
/// <summary>
/// Gets a collection of objects that the selected items in an ItemsControl are bound to.
/// </summary>
///
/// <remarks>
/// If the control that initated the drag is unbound or not an ItemsControl, this will be empty.
/// </remarks>
IEnumerable SourceItems { get; }
/// <summary>
/// Gets the group from a dragged item if the drag is currently from an ItemsControl with groups.
/// </summary>
CollectionViewGroup SourceGroup { get; }
/// <summary>
/// Gets the control that initiated the drag.
/// </summary>
UIElement VisualSource { get; }
/// <summary>
/// Gets the item in an ItemsControl that started the drag.
/// </summary>
///
/// <remarks>
/// If the control that initiated the drag is an ItemsControl, this property will hold the item
/// container of the clicked item. For example, if <see cref="VisualSource"/> is a ListBox this
/// will hold a ListBoxItem.
/// </remarks>
UIElement VisualSourceItem { get; }
/// <summary>
/// Gets the FlowDirection of the current drag source.
/// </summary>
FlowDirection VisualSourceFlowDirection { get; }
/// <summary>
/// Gets the <see cref="IDataObject"/> which is used by the drag and drop operation. Set it to
/// a custom instance if custom drag and drop behavior is needed.
/// </summary>
object DataObject { get; set; }
/// <summary>Initiates a drag-and-drop operation.</summary>
/// <returns>One of the <see cref="T:System.Windows.DragDropEffects" /> values that specifies the final effect that was performed during the drag-and-drop operation.</returns>
Func<DependencyObject, object, DragDropEffects, DragDropEffects> DragDropHandler { get; set; }
/// <summary>
/// Gets the drag drop copy key state indicating the effect of the drag drop operation.
/// </summary>
DragDropKeyStates DragDropCopyKeyState { get; }
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Windows;
namespace GongSolutions.Wpf.DragDrop
{
/// <summary>
/// Interface implemented by Drag Handlers.
/// </summary>
public interface IDragSource
{
/// <summary>
/// Queries whether a drag can be started.
/// </summary>
///
/// <param name="dragInfo">
/// Information about the drag.
/// </param>
///
/// <remarks>
/// To allow a drag to be started, the <see cref="DragInfo.Effects"/> property on <paramref name="dragInfo"/>
/// should be set to a value other than <see cref="DragDropEffects.None"/>.
/// </remarks>
void StartDrag(IDragInfo dragInfo);
/// <summary>
/// With this action it's possible to check if the drag/drop operation is allowed to start
/// e.g. check for a UIElement inside a list view item, that should not start a drag/drop operation
/// </summary>
bool CanStartDrag(IDragInfo dragInfo);
/// <summary>
/// Notifies the drag handler that a drop has occurred.
/// </summary>
///
/// <param name="dropInfo">
/// Information about the drop.
/// </param>
void Dropped(IDropInfo dropInfo);
/// <summary>
/// Notifies the drag handler that a drag and drop operation has finished.
/// </summary>
/// <param name="operationResult">The operation result.</param>
/// <param name="dragInfo">The drag information.</param>
void DragDropOperationFinished(DragDropEffects operationResult, IDragInfo dragInfo);
/// <summary>
/// Notifies the drag handler that a drag has been aborted.
/// </summary>
void DragCancelled();
/// <summary>
/// Notifies that an exception has occurred upon dragging.
/// </summary>
/// <param name="exception">
/// The exception that occurrred.
/// </param>
/// <returns>
/// Boolean indicating whether the exception is handled in the drag handler.
/// False will rethrow the exception.
/// </returns>
bool TryCatchOccurredException(Exception exception);
}
}

View File

@@ -0,0 +1,155 @@
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace GongSolutions.Wpf.DragDrop
{
public interface IDropInfo
{
/// <summary>
/// Gets the drag data.
/// </summary>
///
/// <remarks>
/// If the drag came from within the framework, this will hold:
///
/// - The dragged data if a single item was dragged.
/// - A typed IEnumerable if multiple items were dragged.
/// </remarks>
object Data { get; }
/// <summary>
/// Gets a <see cref="DragInfo"/> object holding information about the source of the drag,
/// if the drag came from within the framework.
/// </summary>
IDragInfo DragInfo { get; }
/// <summary>
/// Gets the mouse position relative to the VisualTarget
/// </summary>
Point DropPosition { get; }
/// <summary>
/// Gets or sets the class of drop target to display.
/// </summary>
///
/// <remarks>
/// The standard drop target adorner classes are held in the <see cref="DropTargetAdorners"/>
/// class.
/// </remarks>
Type DropTargetAdorner { get; set; }
/// <summary>
/// Gets or sets the allowed effects for the drop.
/// </summary>
///
/// <remarks>
/// This must be set to a value other than <see cref="DragDropEffects.None"/> by a drop handler in order
/// for a drop to be possible.
/// </remarks>
DragDropEffects Effects { get; set; }
/// <summary>
/// Gets the current insert position within <see cref="TargetCollection"/>.
/// </summary>
int InsertIndex { get; }
/// <summary>
/// Gets the current insert position within the source (unfiltered) <see cref="TargetCollection"/>.
/// </summary>
/// <remarks>
/// This should be only used in a Drop action.
/// This works only correct with different objects (string, int, etc won't work correct).
/// </remarks>
int UnfilteredInsertIndex { get; }
/// <summary>
/// Gets the collection that the target ItemsControl is bound to.
/// </summary>
///
/// <remarks>
/// If the current drop target is unbound or not an ItemsControl, this will be null.
/// </remarks>
IEnumerable TargetCollection { get; }
/// <summary>
/// Gets the object that the current drop target is bound to.
/// </summary>
///
/// <remarks>
/// If the current drop target is unbound or not an ItemsControl, this will be null.
/// </remarks>
object TargetItem { get; }
/// <summary>
/// Gets the current group target.
/// </summary>
///
/// <remarks>
/// If the drag is currently over an ItemsControl with groups, describes the group that
/// the drag is currently over.
/// </remarks>
CollectionViewGroup TargetGroup { get; }
/// <summary>
/// Gets the control that is the current drop target.
/// </summary>
UIElement VisualTarget { get; }
/// <summary>
/// Gets the item in an ItemsControl that is the current drop target.
/// </summary>
///
/// <remarks>
/// If the current drop target is unbound or not an ItemsControl, this will be null.
/// </remarks>
UIElement VisualTargetItem { get; }
/// <summary>
/// Gets the orientation of the current drop target.
/// </summary>
Orientation VisualTargetOrientation { get; }
/// <summary>
/// Gets the FlowDirection of the current drop target.
/// </summary>
FlowDirection VisualTargetFlowDirection { get; }
/// <summary>
/// Gets and sets the text displayed in the DropDropEffects adorner.
/// </summary>
string DestinationText { get; set; }
/// <summary>
/// Gets and sets the effect text displayed in the DropDropEffects adorner.
/// </summary>
string EffectText { get; set; }
/// <summary>
/// Gets the relative position the item will be inserted to compared to the TargetItem
/// </summary>
RelativeInsertPosition InsertPosition { get; }
/// <summary>
/// Gets a flag enumeration indicating the current state of the SHIFT, CTRL, and ALT keys, as well as the state of the mouse buttons.
/// </summary>
DragDropKeyStates KeyStates { get; }
/// <summary>
/// Indicates if the drop info should be handled by itself (useful for child elements)
/// </summary>
bool NotHandled { get; set; }
/// <summary>
/// Gets a value indicating whether the target is in the same context as the source, <see cref="DragDrop.DragDropContextProperty" />.
/// </summary>
bool IsSameDragDropContextAsSource { get; }
/// <summary>
/// Gets the current mode of the underlying routed event.
/// </summary>
EventType EventType { get; }
}
}

View File

@@ -0,0 +1,34 @@
using System.Windows;
namespace GongSolutions.Wpf.DragDrop
{
/// <summary>
/// Interface implemented by Drop Handlers.
/// </summary>
public interface IDropTarget
{
/// <summary>
/// Updates the current drag state.
/// </summary>
///
/// <param name="dropInfo">
/// Information about the drag.
/// </param>
///
/// <remarks>
/// To allow a drop at the current drag position, the <see cref="DropInfo.Effects"/> property on
/// <paramref name="dropInfo"/> should be set to a value other than <see cref="DragDropEffects.None"/>
/// and <see cref="DropInfo.Data"/> should be set to a non-null value.
/// </remarks>
void DragOver(IDropInfo dropInfo);
/// <summary>
/// Performs a drop.
/// </summary>
///
/// <param name="dropInfo">
/// Information about the drop.
/// </param>
void Drop(IDropInfo dropInfo);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

View File

@@ -0,0 +1,111 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace GongSolutions.Wpf.DragDrop.Icons
{
/// <summary>
/// Static class to provide access to standard application images
/// </summary>
public static class IconFactory
{
/// <summary>
/// Gets the icon which can be used for the effect type None.
/// </summary>
public static BitmapImage EffectNone { get; } = GetImage("EffectNone.png", 12);
/// <summary>
/// Gets the icon which can be used for the effect type Copy.
/// </summary>
public static BitmapImage EffectCopy { get; } = GetImage("EffectCopy.png", 12);
/// <summary>
/// Gets the icon which can be used for the effect type Move.
/// </summary>
public static BitmapImage EffectMove { get; } = GetImage("EffectMove.png", 12);
/// <summary>
/// Gets the icon which can be used for the effect type Link.
/// </summary>
public static BitmapImage EffectLink { get; } = GetImage("EffectLink.png", 12);
/// <summary>
/// Loads an image based on the name and size required.
/// Images need to be marked as 'Resource' in the project for them be loaded.
/// </summary>
/// <param name="iconName">Name of the icon</param>
/// <param name="size">The size of the icon</param>
/// <returns>The image to be displayed</returns>
private static BitmapImage GetImage(string iconName, int size)
{
var uri = new Uri($@"pack://application:,,,/GongSolutions.Wpf.DragDrop;component/Icons/{iconName}", UriKind.RelativeOrAbsolute);
var icon = new BitmapImage(uri);
icon.DecodePixelHeight = size;
icon.DecodePixelWidth = size;
return icon;
}
/// <summary>
/// Creates a custom cursor.
/// </summary>
/// <param name="rx"></param>
/// <param name="ry"></param>
/// <param name="brush"></param>
/// <param name="pen"></param>
/// <returns></returns>
public static Cursor CreateCursor(double rx, double ry, SolidColorBrush brush, Pen pen)
{
var vis = new DrawingVisual();
using (var dc = vis.RenderOpen())
{
dc.DrawRectangle(brush, new Pen(Brushes.Black, 0.1), new Rect(0, 0, rx, ry));
dc.Close();
}
var rtb = new RenderTargetBitmap(64, 64, 96, 96, PixelFormats.Pbgra32);
rtb.Render(vis);
using (var ms1 = new MemoryStream())
{
var penc = new PngBitmapEncoder();
penc.Frames.Add(BitmapFrame.Create(rtb));
penc.Save(ms1);
var pngBytes = ms1.ToArray();
var size = pngBytes.GetLength(0);
//.cur format spec http://en.wikipedia.org/wiki/ICO_(file_format)
using (var ms = new MemoryStream())
{
{
//ICONDIR Structure
ms.Write(BitConverter.GetBytes((Int16)0), 0, 2); //Reserved must be zero; 2 bytes
ms.Write(BitConverter.GetBytes((Int16)2), 0, 2); //image type 1 = ico 2 = cur; 2 bytes
ms.Write(BitConverter.GetBytes((Int16)1), 0, 2); //number of images; 2 bytes
}
{
//ICONDIRENTRY structure
ms.WriteByte(32); //image width in pixels
ms.WriteByte(32); //image height in pixels
ms.WriteByte(0); //Number of Colors in the color palette. Should be 0 if the image doesn't use a color palette
ms.WriteByte(0); //reserved must be 0
ms.Write(BitConverter.GetBytes((Int16)(rx / 2.0)), 0, 2); //2 bytes. In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left.
ms.Write(BitConverter.GetBytes((Int16)(ry / 2.0)), 0, 2); //2 bytes. In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top.
ms.Write(BitConverter.GetBytes(size), 0, 4); //Specifies the size of the image's data in bytes
ms.Write(BitConverter.GetBytes((Int32)22), 0, 4); //Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file
}
ms.Write(pngBytes, 0, size); //write the png data.
ms.Seek(0, SeekOrigin.Begin);
return new Cursor(ms);
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
using System.Runtime.InteropServices;
using System.Windows.Markup;
[assembly: XmlnsPrefix("urn:gong-wpf-dragdrop", "dd")]
[assembly: XmlnsDefinition("urn:gong-wpf-dragdrop", "GongSolutions.Wpf.DragDrop")]
[assembly: XmlnsDefinition("urn:gong-wpf-dragdrop", "GongSolutions.Wpf.DragDrop.Utilities")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("940084f7-d48e-41b3-9e0d-cf574d587643")]

View File

@@ -0,0 +1,13 @@
namespace GongSolutions.Wpf.DragDrop
{
/// <summary>
/// Specifies how <see cref="T:System.Windows.Controls.ScrollViewer" /> reacts to drop operation.
/// </summary>
public enum ScrollingMode
{
None,
HorizontalOnly,
VerticalOnly,
Both
}
}

View File

@@ -0,0 +1,96 @@
namespace GongSolutions.Wpf.DragDrop.Utilities
{
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
/// <summary>
/// A helper class for Dpi logicm cause Microsoft hides this with the internal flag.
/// </summary>
public static class DpiHelper
{
private static Matrix _transformToDevice;
private static Matrix _transformToLogical;
public static double DpiX = 0d;
public static double DpiY = 0d;
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
static DpiHelper()
{
var dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic | BindingFlags.Static);
var dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", BindingFlags.NonPublic | BindingFlags.Static);
var pixelsPerInchX = (int)dpiXProperty.GetValue(null, null); //SystemParameters.DpiX;
DpiX = (double)pixelsPerInchX;
var pixelsPerInchY = (int)dpiYProperty.GetValue(null, null); //SystemParameters.Dpi;
DpiY = (double)pixelsPerInchY;
_transformToLogical = Matrix.Identity;
_transformToLogical.Scale(96d / (double)pixelsPerInchX, 96d / (double)pixelsPerInchY);
_transformToDevice = Matrix.Identity;
_transformToDevice.Scale((double)pixelsPerInchX / 96d, (double)pixelsPerInchY / 96d);
}
/// <summary>
/// Convert a point in device independent pixels (1/96") to a point in the system coordinates.
/// </summary>
/// <param name="logicalPoint">A point in the logical coordinate system.</param>
/// <returns>Returns the point converted to the system's coordinates.</returns>
public static Point LogicalPixelsToDevice(Point logicalPoint)
{
return _transformToDevice.Transform(logicalPoint);
}
/// <summary>
/// Convert a point in system coordinates to a point in device independent pixels (1/96").
/// </summary>
/// <param name="devicePoint">A point in the physical coordinate system.</param>
/// <returns>Returns the point converted to the device independent coordinate system.</returns>
public static Point DevicePixelsToLogical(Point devicePoint)
{
return _transformToLogical.Transform(devicePoint);
}
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
public static Rect LogicalRectToDevice(Rect logicalRectangle)
{
Point topLeft = LogicalPixelsToDevice(new Point(logicalRectangle.Left, logicalRectangle.Top));
Point bottomRight = LogicalPixelsToDevice(new Point(logicalRectangle.Right, logicalRectangle.Bottom));
return new Rect(topLeft, bottomRight);
}
public static Rect DeviceRectToLogical(Rect deviceRectangle)
{
Point topLeft = DevicePixelsToLogical(new Point(deviceRectangle.Left, deviceRectangle.Top));
Point bottomRight = DevicePixelsToLogical(new Point(deviceRectangle.Right, deviceRectangle.Bottom));
return new Rect(topLeft, bottomRight);
}
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
public static Size LogicalSizeToDevice(Size logicalSize)
{
Point pt = LogicalPixelsToDevice(new Point(logicalSize.Width, logicalSize.Height));
return new Size { Width = pt.X, Height = pt.Y };
}
public static Size DeviceSizeToLogical(Size deviceSize)
{
Point pt = DevicePixelsToLogical(new Point(deviceSize.Width, deviceSize.Height));
return new Size(pt.X, pt.Y);
}
public static Thickness LogicalThicknessToDevice(Thickness logicalThickness)
{
Point topLeft = LogicalPixelsToDevice(new Point(logicalThickness.Left, logicalThickness.Top));
Point bottomRight = LogicalPixelsToDevice(new Point(logicalThickness.Right, logicalThickness.Bottom));
return new Thickness(topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y);
}
}
}

View File

@@ -0,0 +1,127 @@
using System.Windows;
using System.Windows.Media;
namespace GongSolutions.Wpf.DragDrop.Utilities
{
using System;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
public static class DragDropExtensions
{
/// <summary>
/// Determines whether the given element is ignored on drag start (<see cref="DragDrop.DragSourceIgnoreProperty"/>).
/// </summary>
/// <param name="element">The given element.</param>
/// <returns>Element is ignored or not.</returns>
public static bool IsDragSourceIgnored(this UIElement element)
{
return element != null && DragDrop.GetDragSourceIgnore(element);
}
/// <summary>
/// Determines whether the given element is ignored on drop action (<see cref="DragDrop.IsDragSourceProperty"/>).
/// </summary>
/// <param name="element">The given element.</param>
/// <returns>Element is ignored or not.</returns>
public static bool IsDragSource(this UIElement element)
{
return element != null && DragDrop.GetIsDragSource(element);
}
/// <summary>
/// Determines whether the given element is ignored on drop action (<see cref="DragDrop.IsDropTargetProperty"/>).
/// </summary>
/// <param name="element">The given element.</param>
/// <returns>Element is ignored or not.</returns>
public static bool IsDropTarget(this UIElement element)
{
return element != null && DragDrop.GetIsDropTarget(element);
}
/// <summary>
/// Gets if drop position is directly over element
/// </summary>
/// <param name="dropPosition">Drop position</param>
/// <param name="element">element to check whether or not the drop position is directly over or not</param>
/// <param name="relativeToElement">element to which the drop position is related</param>
/// <returns>drop position is directly over element or not</returns>
public static bool DirectlyOverElement(this Point dropPosition, UIElement element, UIElement relativeToElement)
{
if (element == null)
return false;
var relativeItemPosition = element.TranslatePoint(new Point(0, 0), relativeToElement);
var relativeDropPosition = new Point(dropPosition.X - relativeItemPosition.X, dropPosition.Y - relativeItemPosition.Y);
return VisualTreeHelper.GetDescendantBounds(element).Contains(relativeDropPosition);
}
/// <summary>
/// Capture screen and create data template containing the captured image
/// </summary>
/// <param name="element">visual source to capture screen of</param>
/// <param name="visualSourceFlowDirection">Flowdirection of visual source</param>
/// <returns></returns>
public static DataTemplate GetCaptureScreenDataTemplate(this UIElement element, FlowDirection visualSourceFlowDirection)
{
DataTemplate template = null;
var bs = CaptureScreen(element, visualSourceFlowDirection);
if (bs != null)
{
var factory = new FrameworkElementFactory(typeof(Image));
factory.SetValue(Image.SourceProperty, bs);
factory.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
factory.SetValue(RenderOptions.BitmapScalingModeProperty, BitmapScalingMode.HighQuality);
factory.SetValue(FrameworkElement.WidthProperty, bs.Width);
factory.SetValue(FrameworkElement.HeightProperty, bs.Height);
factory.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Left);
factory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Top);
template = new DataTemplate { VisualTree = factory };
}
return template;
}
// Helper to generate the image - I grabbed this off Google
// somewhere. -- Chris Bordeman cbordeman@gmail.com
private static BitmapSource CaptureScreen(Visual target, FlowDirection flowDirection)
{
if (target == null)
{
return null;
}
var dpiX = DpiHelper.DpiX;
var dpiY = DpiHelper.DpiY;
var bounds = VisualTreeHelper.GetDescendantBounds(target);
var dpiBounds = DpiHelper.LogicalRectToDevice(bounds);
var pixelWidth = (int)Math.Ceiling(dpiBounds.Width);
var pixelHeight = (int)Math.Ceiling(dpiBounds.Height);
if (pixelWidth < 0 || pixelHeight < 0)
{
return null;
}
var rtb = new RenderTargetBitmap(pixelWidth, pixelHeight, dpiX, dpiY, PixelFormats.Pbgra32);
var dv = new DrawingVisual();
using (var ctx = dv.RenderOpen())
{
var vb = new VisualBrush(target);
if (flowDirection == FlowDirection.RightToLeft)
{
var transformGroup = new TransformGroup();
transformGroup.Children.Add(new ScaleTransform(-1, 1));
transformGroup.Children.Add(new TranslateTransform(bounds.Size.Width - 1, 0));
ctx.PushTransform(transformGroup);
}
ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);
return rtb;
}
}
}

View File

@@ -0,0 +1,135 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Controls.Primitives;
namespace GongSolutions.Wpf.DragDrop.Utilities
{
public static class HitTestUtilities
{
public static bool HitTest4Type<T>(object sender, Point elementPosition)
where T : UIElement
{
var uiElement = GetHitTestElement4Type<T>(sender, elementPosition);
return uiElement != null && uiElement.Visibility == Visibility.Visible;
}
private static T GetHitTestElement4Type<T>(object sender, Point elementPosition)
where T : UIElement
{
var visual = sender as Visual;
if (visual == null)
{
return null;
}
var hit = VisualTreeHelper.HitTest(visual, elementPosition);
if (hit == null)
{
return null;
}
var uiElement = hit.VisualHit.GetVisualAncestor<T>();
return uiElement;
}
public static bool HitTest4GridViewColumnHeader(object sender, Point elementPosition)
{
if (sender is ListView)
{
// no drag&drop for column header
var columnHeader = GetHitTestElement4Type<GridViewColumnHeader>(sender, elementPosition);
if (columnHeader != null && (columnHeader.Role == GridViewColumnHeaderRole.Floating || columnHeader.Visibility == Visibility.Visible))
{
return true;
}
}
return false;
}
public static bool HitTest4DataGridTypes(object sender, Point elementPosition)
{
if (sender is DataGrid)
{
// no drag&drop for column header
var columnHeader = GetHitTestElement4Type<DataGridColumnHeader>(sender, elementPosition);
if (columnHeader != null && columnHeader.Visibility == Visibility.Visible)
{
return true;
}
// no drag&drop for row header
var rowHeader = GetHitTestElement4Type<DataGridRowHeader>(sender, elementPosition);
if (rowHeader != null && rowHeader.Visibility == Visibility.Visible)
{
// no drag&drop for row header gripper
var thumb = GetHitTestElement4Type<Thumb>(sender, elementPosition);
if (thumb != null)
{
return true;
}
}
// drag&drop only for data grid row
var dataRow = GetHitTestElement4Type<DataGridRow>(sender, elementPosition);
return dataRow == null || Equals(dataRow.DataContext, CollectionView.NewItemPlaceholder);
}
return false;
}
public static bool HitTest4DataGridTypesOnDragOver(object sender, Point elementPosition)
{
if (sender is DataGrid)
{
// no drag&drop on column header
var columnHeader = GetHitTestElement4Type<DataGridColumnHeader>(sender, elementPosition);
if (columnHeader != null && columnHeader.Visibility == Visibility.Visible)
{
return true;
}
}
return false;
}
/// <summary>
/// thx to @osicka from issue #84
///
/// e.g. original source is part of a popup (e.g. ComboBox drop down), the hit test needs to be done on the original source.
/// Because the popup is not attached to the visual tree of the sender.
/// This function test this by looping back from the original source to the sender and if it didn't end up at the sender stopped the drag.
/// </summary>
public static bool IsNotPartOfSender(object sender, MouseButtonEventArgs e)
{
var visual = e.OriginalSource as Visual;
if (visual == null)
{
return false;
}
var hit = VisualTreeHelper.HitTest(visual, e.GetPosition((IInputElement)visual));
if (hit == null)
{
return false;
}
else
{
var depObj = e.OriginalSource as DependencyObject;
if (depObj == null)
{
return false;
}
if (depObj == sender)
{
return false;
}
var item = VisualTreeHelper.GetParent(depObj.FindVisualTreeRoot());
//var item = VisualTreeHelper.GetParent(e.OriginalSource as DependencyObject);
while (item != null && item != sender)
{
item = VisualTreeHelper.GetParent(item);
}
return item != sender;
}
}
}
}

View File

@@ -0,0 +1,622 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Reflection;
using System.Collections;
using System.Windows.Controls.Primitives;
namespace GongSolutions.Wpf.DragDrop.Utilities
{
public static class ItemsControlExtensions
{
public static CollectionViewGroup FindGroup(this ItemsControl itemsControl, Point position)
{
if (itemsControl.Items.Groups == null || itemsControl.Items.Groups.Count == 0)
{
return null;
}
var element = itemsControl.InputHitTest(position) as DependencyObject;
if (element != null)
{
var groupItem = element.GetVisualAncestor<GroupItem>();
// drag after last item - get group of it
if (groupItem == null && itemsControl.Items.Count > 0)
{
var lastItem = itemsControl.ItemContainerGenerator.ContainerFromItem(itemsControl.Items.GetItemAt(itemsControl.Items.Count - 1)) as FrameworkElement;
if (lastItem != null)
{
var itemEndpoint = lastItem.PointToScreen(new Point(lastItem.ActualWidth, lastItem.ActualHeight));
var positionToScreen = itemsControl.PointToScreen(position);
switch (itemsControl.GetItemsPanelOrientation())
{
case Orientation.Horizontal:
// assume LeftToRight
groupItem = itemEndpoint.X <= positionToScreen.X ? lastItem.GetVisualAncestor<GroupItem>() : null;
break;
case Orientation.Vertical:
groupItem = itemEndpoint.Y <= positionToScreen.Y ? lastItem.GetVisualAncestor<GroupItem>() : null;
break;
}
}
}
if (groupItem != null)
{
return groupItem.Content as CollectionViewGroup;
}
}
return null;
}
public static bool CanSelectMultipleItems(this ItemsControl itemsControl)
{
if (itemsControl is MultiSelector multiSelector)
{
// The CanSelectMultipleItems property is protected. Use reflection to
// get its value anyway.
var propertyInfo = multiSelector.GetType().GetProperty("CanSelectMultipleItems", BindingFlags.Instance | BindingFlags.NonPublic);
return propertyInfo != null && (bool)propertyInfo.GetValue(multiSelector, null);
}
else if (itemsControl is ListBox listBox)
{
return listBox.SelectionMode != SelectionMode.Single;
}
else
{
return false;
}
}
public static UIElement GetItemContainer(this ItemsControl itemsControl, DependencyObject child)
{
bool isItemContainer;
var itemType = GetItemContainerType(itemsControl, out isItemContainer);
if (itemType != null)
{
return isItemContainer
? (UIElement)child.GetVisualAncestor(itemType, itemsControl)
: (UIElement)child.GetVisualAncestor(itemType, itemsControl, itemsControl.GetType());
}
return null;
}
public static UIElement GetItemContainerAt(this ItemsControl itemsControl, Point position)
{
var inputElement = itemsControl.InputHitTest(position);
var uiElement = inputElement as UIElement;
if (uiElement != null)
{
return GetItemContainer(itemsControl, uiElement);
}
// ContentElement's such as Run's within TextBlock's could not be used as drop target items, because they are not UIElement's.
var contentElement = inputElement as ContentElement;
if (contentElement != null)
{
return GetItemContainer(itemsControl, contentElement);
}
return null;
}
public static UIElement GetItemContainerAt(this ItemsControl itemsControl, Point position, Orientation searchDirection)
{
bool isItemContainer;
var itemContainerType = GetItemContainerType(itemsControl, out isItemContainer);
Geometry hitTestGeometry;
if (typeof(TreeViewItem).IsAssignableFrom(itemContainerType))
{
hitTestGeometry = new LineGeometry(new Point(0, position.Y), new Point(itemsControl.RenderSize.Width, position.Y));
}
else
{
var geometryGroup = new GeometryGroup();
geometryGroup.Children.Add(new LineGeometry(new Point(0, position.Y), new Point(itemsControl.RenderSize.Width, position.Y)));
geometryGroup.Children.Add(new LineGeometry(new Point(position.X, 0), new Point(position.X, itemsControl.RenderSize.Height)));
hitTestGeometry = geometryGroup;
}
var hits = new HashSet<DependencyObject>();
VisualTreeHelper.HitTest(itemsControl,
obj =>
{
// Viewport3D is not good for us
// Stop on ScrollBar to improve performance (e.g. at DataGrid)
if (obj is Viewport3D || (itemsControl is DataGrid && obj is ScrollBar))
{
return HitTestFilterBehavior.Stop;
}
return HitTestFilterBehavior.Continue;
},
result =>
{
var itemContainer = isItemContainer
? result.VisualHit.GetVisualAncestor(itemContainerType, itemsControl)
: result.VisualHit.GetVisualAncestor(itemContainerType, itemsControl, itemsControl.GetType());
if (itemContainer != null && ((UIElement)itemContainer).IsVisible == true)
{
var tvItem = itemContainer as TreeViewItem;
if (tvItem != null)
{
var tv = tvItem.GetVisualAncestor<TreeView>();
if (tv == itemsControl)
{
hits.Add(itemContainer);
}
}
else
{
if (itemsControl.ItemContainerGenerator.IndexFromContainer(itemContainer) >= 0)
{
hits.Add(itemContainer);
}
}
}
return HitTestResultBehavior.Continue;
},
new GeometryHitTestParameters(hitTestGeometry));
return GetClosest(itemsControl, hits, position, searchDirection);
}
public static Type GetItemContainerType(this ItemsControl itemsControl, out bool isItemContainer)
{
// determines if the itemsControl is not a ListView, ListBox or TreeView
isItemContainer = false;
if (typeof(TabControl).IsAssignableFrom(itemsControl.GetType()))
{
return typeof(TabItem);
}
if (typeof(DataGrid).IsAssignableFrom(itemsControl.GetType()))
{
return typeof(DataGridRow);
}
// There is no safe way to get the item container type for an ItemsControl.
// First hard-code the types for the common ItemsControls.
//if (itemsControl.GetType().IsAssignableFrom(typeof(ListView)))
if (typeof(ListView).IsAssignableFrom(itemsControl.GetType()))
{
return typeof(ListViewItem);
}
//if (itemsControl.GetType().IsAssignableFrom(typeof(ListBox)))
else if (typeof(ListBox).IsAssignableFrom(itemsControl.GetType()))
{
return typeof(ListBoxItem);
}
//else if (itemsControl.GetType().IsAssignableFrom(typeof(TreeView)))
else if (typeof(TreeView).IsAssignableFrom(itemsControl.GetType()))
{
return typeof(TreeViewItem);
}
// Otherwise look for the control's ItemsPresenter, get it's child panel and the first
// child of that *should* be an item container.
//
// If the control currently has no items, we're out of luck.
if (itemsControl.Items.Count > 0)
{
var itemsPresenters = itemsControl.GetVisualDescendents<ItemsPresenter>();
foreach (var itemsPresenter in itemsPresenters)
{
if (VisualTreeHelper.GetChildrenCount(itemsPresenter) > 0)
{
var panel = VisualTreeHelper.GetChild(itemsPresenter, 0);
var itemContainer = VisualTreeHelper.GetChildrenCount(panel) > 0
? VisualTreeHelper.GetChild(panel, 0)
: null;
// Ensure that this actually *is* an item container by checking it with
// ItemContainerGenerator.
if (itemContainer != null &&
!(itemContainer is GroupItem) &&
itemsControl.ItemContainerGenerator.IndexFromContainer(itemContainer) != -1)
{
isItemContainer = true;
return itemContainer.GetType();
}
}
}
}
return null;
}
/// <summary>
/// Gets the Orientation which will be used for the drag drop action.
/// Normally it will be look up to find the correct orientaion of the inner ItemsPanel,
/// but sometimes it's necessary to force the oreintation, if the look up is wrong.
/// If so, the ItemsPanelOrientation value is taken.
/// </summary>
/// <param name="itemsControl">The ItemsControl for the look up.</param>
/// <returns>Orientation for the given ItemsControl.</returns>
public static Orientation GetItemsPanelOrientation(this ItemsControl itemsControl)
{
var itemsPanelOrientation = DragDrop.GetItemsPanelOrientation(itemsControl);
if (itemsPanelOrientation.HasValue)
{
return itemsPanelOrientation.Value;
}
if (itemsControl is TabControl)
{
//HitTestUtilities.HitTest4Type<TabPanel>(sender, elementPosition)
//var tabPanel = itemsControl.GetVisualDescendent<TabPanel>();
var tabControl = (TabControl)itemsControl;
return tabControl.TabStripPlacement == Dock.Left || tabControl.TabStripPlacement == Dock.Right ? Orientation.Vertical : Orientation.Horizontal;
}
var itemsPresenter = itemsControl.GetVisualDescendent<ItemsPresenter>() ?? itemsControl.GetVisualDescendent<ScrollContentPresenter>() as UIElement;
if (itemsPresenter != null && VisualTreeHelper.GetChildrenCount(itemsPresenter) > 0)
{
var itemsPanel = VisualTreeHelper.GetChild(itemsPresenter, 0);
var orientationProperty = itemsPanel.GetType().GetProperty("Orientation", typeof(Orientation));
if (orientationProperty != null)
{
return (Orientation)orientationProperty.GetValue(itemsPanel, null);
}
}
// Make a guess!
return Orientation.Vertical;
}
/// <summary>
/// Gets the FlowDirection which will be used for the drag drop action.
/// </summary>
/// <param name="itemsControl">The ItemsControl for the look up.</param>
/// <returns>FlowDirection for the given ItemsControl.</returns>
public static FlowDirection GetItemsPanelFlowDirection(this ItemsControl itemsControl)
{
var itemsPresenter = itemsControl.GetVisualDescendent<ItemsPresenter>() ?? itemsControl.GetVisualDescendent<ScrollContentPresenter>() as UIElement;
if (itemsPresenter != null && VisualTreeHelper.GetChildrenCount(itemsPresenter) > 0)
{
var itemsPanel = VisualTreeHelper.GetChild(itemsPresenter, 0);
var flowDirectionProperty = itemsPanel.GetType().GetProperty("FlowDirection", typeof(FlowDirection));
if (flowDirectionProperty != null)
{
return (FlowDirection)flowDirectionProperty.GetValue(itemsPanel, null);
}
}
// Make a guess!
return FlowDirection.LeftToRight;
}
/// <summary>
/// Sets the given object as selected item at the ItemsControl.
/// </summary>
/// <param name="itemsControl">The ItemsControl which contains the item.</param>
/// <param name="item">The object which should be selected.</param>
public static void SetSelectedItem(this ItemsControl itemsControl, object item)
{
if (itemsControl is MultiSelector)
{
((MultiSelector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
((MultiSelector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, item);
}
else if (itemsControl is ListBox)
{
var selectionMode = ((ListBox)itemsControl).SelectionMode;
try
{
// change SelectionMode for UpdateAnchorAndActionItem
((ListBox)itemsControl).SetCurrentValue(ListBox.SelectionModeProperty, SelectionMode.Single);
((ListBox)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
((ListBox)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, item);
}
finally
{
((ListBox)itemsControl).SetCurrentValue(ListBox.SelectionModeProperty, selectionMode);
}
}
else if (itemsControl is TreeViewItem)
{
// clear old selected item
var treeView = ItemsControl.ItemsControlFromItemContainer((TreeViewItem)itemsControl);
if (treeView != null)
{
var prevSelectedItem = treeView.GetValue(TreeView.SelectedItemProperty);
if (prevSelectedItem != null)
{
var prevSelectedTreeViewItem = treeView.ItemContainerGenerator.ContainerFromItem(prevSelectedItem) as TreeViewItem;
if (prevSelectedTreeViewItem != null)
{
prevSelectedTreeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, false);
}
}
}
// set new selected item
// TreeView.SelectedItemProperty is a read only property, so we must set the selection on the TreeViewItem itself
var treeViewItem = ((TreeViewItem)itemsControl).ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (treeViewItem != null)
{
treeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, true);
}
}
else if (itemsControl is TreeView)
{
// clear old selected item
var prevSelectedItem = ((TreeView)itemsControl).GetValue(TreeView.SelectedItemProperty);
if (prevSelectedItem != null)
{
var prevSelectedTreeViewItem = ((TreeView)itemsControl).ItemContainerGenerator.ContainerFromItem(prevSelectedItem) as TreeViewItem;
if (prevSelectedTreeViewItem != null)
{
prevSelectedTreeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, false);
}
}
// set new selected item
// TreeView.SelectedItemProperty is a read only property, so we must set the selection on the TreeViewItem itself
var treeViewItem = ((TreeView)itemsControl).ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (treeViewItem != null)
{
treeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, true);
}
}
else if (itemsControl is Selector)
{
((Selector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
((Selector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, item);
}
}
/// <summary>
/// Clears the selected items.
/// </summary>
/// <param name="itemsControl">The items control.</param>
public static void ClearSelectedItems(this ItemsControl itemsControl)
{
if (itemsControl is MultiSelector)
{
if (((MultiSelector)itemsControl).CanSelectMultipleItems())
{
((MultiSelector)itemsControl).SelectedItems.Clear();
}
((MultiSelector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
}
else if (itemsControl is ListBox)
{
if (((ListBox)itemsControl).CanSelectMultipleItems())
{
((ListBox)itemsControl).SelectedItems.Clear();
((ListBox)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
}
}
else if (itemsControl is TreeViewItem)
{
var treeView = ItemsControl.ItemsControlFromItemContainer((TreeViewItem)itemsControl);
treeView?.ClearSelectedItems();
}
else if (itemsControl is TreeView)
{
// clear old selected item
var prevSelectedItem = ((TreeView)itemsControl).GetValue(TreeView.SelectedItemProperty);
if (prevSelectedItem != null)
{
var prevSelectedTreeViewItem = ((TreeView)itemsControl).ItemContainerGenerator.ContainerFromItem(prevSelectedItem) as TreeViewItem;
if (prevSelectedTreeViewItem != null)
{
prevSelectedTreeViewItem.SetCurrentValue(TreeViewItem.IsSelectedProperty, false);
}
}
}
else if (itemsControl is Selector)
{
((Selector)itemsControl).SetCurrentValue(Selector.SelectedItemProperty, null);
}
}
public static object GetSelectedItem(this ItemsControl itemsControl)
{
if (itemsControl is MultiSelector)
{
return ((MultiSelector)itemsControl).SelectedItem;
}
else if (itemsControl is ListBox)
{
return ((ListBox)itemsControl).SelectedItem;
}
else if (itemsControl is TreeView)
{
return ((TreeView)itemsControl).GetValue(TreeView.SelectedItemProperty);
}
else if (itemsControl is Selector)
{
return ((Selector)itemsControl).SelectedItem;
}
return null;
}
public static IEnumerable GetSelectedItems(this ItemsControl itemsControl)
{
//if (itemsControl.GetType().IsAssignableFrom(typeof(MultiSelector)))
if (typeof(MultiSelector).IsAssignableFrom(itemsControl.GetType()))
{
return ((MultiSelector)itemsControl).SelectedItems;
}
else if (itemsControl is ListBox)
{
var listBox = (ListBox)itemsControl;
if (listBox.SelectionMode == SelectionMode.Single)
{
return Enumerable.Repeat(listBox.SelectedItem, 1);
}
else
{
return listBox.SelectedItems;
}
}
//else if (itemsControl.GetType().IsAssignableFrom(typeof(TreeView)))
else if (typeof(TreeView).IsAssignableFrom(itemsControl.GetType()))
{
return Enumerable.Repeat(((TreeView)itemsControl).SelectedItem, 1);
}
//else if (itemsControl.GetType().IsAssignableFrom(typeof(Selector)))
else if (typeof(Selector).IsAssignableFrom(itemsControl.GetType()))
{
return Enumerable.Repeat(((Selector)itemsControl).SelectedItem, 1);
}
else
{
return Enumerable.Empty<object>();
}
}
public static bool GetItemSelected(this ItemsControl itemsControl, object item)
{
if (itemsControl is MultiSelector)
{
return ((MultiSelector)itemsControl).SelectedItems.Contains(item);
}
else if (itemsControl is ListBox)
{
return ((ListBox)itemsControl).SelectedItems.Contains(item);
}
else if (itemsControl is TreeView)
{
return ((TreeView)itemsControl).SelectedItem == item;
}
else if (itemsControl is Selector)
{
return ((Selector)itemsControl).SelectedItem == item;
}
else
{
return false;
}
}
public static void SetItemSelected(this ItemsControl itemsControl, object item, bool itemSelected)
{
if (itemsControl is MultiSelector)
{
var multiSelector = (MultiSelector)itemsControl;
if (multiSelector.CanSelectMultipleItems())
{
if (itemSelected)
{
multiSelector.SelectedItems.Add(item);
}
else
{
multiSelector.SelectedItems.Remove(item);
}
}
else
{
multiSelector.SetCurrentValue(Selector.SelectedItemProperty, null);
if (itemSelected)
{
multiSelector.SetCurrentValue(Selector.SelectedItemProperty, item);
}
}
}
else if (itemsControl is ListBox)
{
var listBox = (ListBox)itemsControl;
if (listBox.SelectionMode != SelectionMode.Single)
{
if (itemSelected)
{
listBox.SelectedItems.Add(item);
}
else
{
listBox.SelectedItems.Remove(item);
}
}
else
{
listBox.SetCurrentValue(Selector.SelectedItemProperty, null);
if (itemSelected)
{
listBox.SetCurrentValue(Selector.SelectedItemProperty, item);
}
}
}
else
{
if (itemSelected)
{
itemsControl.SetSelectedItem(item);
}
else
{
itemsControl.SetSelectedItem(null);
}
}
}
private static UIElement GetClosest(ItemsControl itemsControl, IEnumerable<DependencyObject> items,
Point position, Orientation searchDirection)
{
//Console.WriteLine("GetClosest - {0}", itemsControl.ToString());
UIElement closest = null;
var closestDistance = double.MaxValue;
foreach (var i in items)
{
var uiElement = i as UIElement;
if (uiElement != null)
{
var p = uiElement.TransformToAncestor(itemsControl).Transform(new Point(0, 0));
var distance = double.MaxValue;
if (itemsControl is TreeView)
{
var xDiff = position.X - p.X;
var yDiff = position.Y - p.Y;
var hyp = Math.Sqrt(Math.Pow(xDiff, 2d) + Math.Pow(yDiff, 2d));
distance = Math.Abs(hyp);
}
else
{
var itemParent = ItemsControl.ItemsControlFromItemContainer(uiElement);
if (itemParent != null && itemParent != itemsControl)
{
searchDirection = itemParent.GetItemsPanelOrientation();
}
switch (searchDirection)
{
case Orientation.Horizontal:
distance = position.X <= p.X ? p.X - position.X : position.X - uiElement.RenderSize.Width - p.X;
break;
case Orientation.Vertical:
distance = position.Y <= p.Y ? p.Y - position.Y : position.Y - uiElement.RenderSize.Height - p.Y;
break;
}
}
if (distance < closestDistance)
{
closest = uiElement;
closestDistance = distance;
}
}
}
return closest;
}
}
}

View File

@@ -0,0 +1,31 @@
using System.Windows;
using System.Windows.Controls;
namespace GongSolutions.Wpf.DragDrop.Utilities
{
public static class RootElementFinder
{
public static UIElement FindRoot(DependencyObject visual)
{
var parentWindow = Window.GetWindow(visual);
var rootElement = parentWindow != null ? parentWindow.Content as UIElement : null;
if (rootElement == null)
{
if (Application.Current != null && Application.Current.MainWindow != null)
{
rootElement = Application.Current.MainWindow.Content as UIElement;
}
if (rootElement == null)
{
rootElement = visual.GetVisualAncestor<Page>() ?? visual.GetVisualAncestor<UserControl>() as UIElement;
}
}
// i don't want the fu... windows forms reference
// if (rootElement == null) {
// var elementHost = m_DragInfo.VisualSource.GetVisualAncestor<ElementHost>();
// rootElement = elementHost != null ? elementHost.Child : null;
// }
return rootElement;
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Windows;
using System.Windows.Controls;
namespace GongSolutions.Wpf.DragDrop.Utilities
{
/// <summary>
/// Extension methods for TreeViewItem
/// </summary>
public static class TreeViewItemExtensions
{
/// <summary>
/// Try get the height of the header part for the given TreeViewItem.
/// If there is no PART_Header it will return Size.Empty.
/// </summary>
/// <param name="item">The TreeViewItem.</param>
public static Size GetHeaderSize(this TreeViewItem item)
{
if (item == null)
{
return Size.Empty;
}
var header = GetHeaderControl(item);
return header != null ? new Size(header.ActualWidth, header.ActualHeight) : item.RenderSize;
}
/// <summary>
/// Try get the header part of the given TreeViewItem.
/// If there is no PART_Header it will return null.
/// </summary>
/// <param name="item">The TreeViewItem.</param>
public static FrameworkElement GetHeaderControl(this TreeViewItem item)
{
return item?.Template?.FindName("PART_Header", item) as FrameworkElement;
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Collections;
namespace GongSolutions.Wpf.DragDrop.Utilities
{
public static class TypeUtilities
{
public static IEnumerable CreateDynamicallyTypedList(IEnumerable source)
{
var type = GetCommonBaseClass(source);
var listType = typeof(List<>).MakeGenericType(type);
var addMethod = listType.GetMethod("Add");
var list = listType.GetConstructor(Type.EmptyTypes).Invoke(null);
foreach (var o in source)
{
addMethod.Invoke(list, new[] { o });
}
return (IEnumerable)list;
}
public static Type GetCommonBaseClass(IEnumerable e)
{
var types = e.Cast<object>().Select(o => o.GetType()).ToArray<Type>();
return GetCommonBaseClass(types);
}
public static Type GetCommonBaseClass(Type[] types)
{
if (types.Length == 0)
{
return typeof(object);
}
var ret = types[0];
for (var i = 1; i < types.Length; ++i)
{
if (types[i].IsAssignableFrom(ret))
{
ret = types[i];
}
else
{
// This will always terminate when ret == typeof(object)
while (!ret.IsAssignableFrom(types[i]))
{
ret = ret.BaseType;
}
}
}
return ret;
}
/// <summary>
/// Gets the enumerable as list.
/// If enumerable is an ICollectionView then it returns the SourceCollection as list.
/// </summary>
/// <param name="enumerable">The enumerable.</param>
/// <returns>Returns a list.</returns>
public static IList TryGetList(this IEnumerable enumerable)
{
if (enumerable is ICollectionView)
{
return ((ICollectionView)enumerable).SourceCollection as IList;
}
else
{
var list = enumerable as IList;
return list ?? (enumerable != null ? enumerable.OfType<object>().ToList() : null);
}
}
}
}

View File

@@ -0,0 +1,169 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Collections.Generic;
using System.Windows.Media.Media3D;
using JetBrains.Annotations;
namespace GongSolutions.Wpf.DragDrop.Utilities
{
public static class VisualTreeExtensions
{
/// <summary>
/// Gets the next ancestor element which is a drop target.
/// </summary>
/// <param name="element">The start element.</param>
/// <returns>The first element which is a drop target.</returns>
public static UIElement TryGetNextAncestorDropTargetElement(this UIElement element)
{
if (element == null)
{
return null;
}
var ancestor = element.GetVisualAncestor<UIElement>();
while (ancestor != null)
{
if (ancestor.IsDropTarget())
{
return ancestor;
}
ancestor = ancestor.GetVisualAncestor<UIElement>();
}
return null;
}
internal static DependencyObject FindVisualTreeRoot(this DependencyObject d)
{
var current = d;
var result = d;
while (current != null)
{
result = current;
if (current is Visual || current is Visual3D)
{
break;
}
else
{
// If we're in Logical Land then we must walk
// up the logical tree until we find a
// Visual/Visual3D to get us back to Visual Land.
current = LogicalTreeHelper.GetParent(current);
}
}
return result;
}
public static T GetVisualAncestor<T>(this DependencyObject d)
where T : class
{
var item = VisualTreeHelper.GetParent(d.FindVisualTreeRoot());
while (item != null)
{
var itemAsT = item as T;
if (itemAsT != null)
{
return itemAsT;
}
item = VisualTreeHelper.GetParent(item);
}
return null;
}
/// <summary>
/// find the visual ancestor item by type
/// </summary>
public static DependencyObject GetVisualAncestor(this DependencyObject d, Type itemSearchType, [NotNull] ItemsControl itemsControl, [NotNull] Type itemContainerSearchType)
{
if (itemsControl == null) throw new ArgumentNullException(nameof(itemsControl));
if (itemContainerSearchType == null) throw new ArgumentNullException(nameof(itemContainerSearchType));
var visualTreeRoot = d.FindVisualTreeRoot();
var currentVisual = VisualTreeHelper.GetParent(visualTreeRoot);
while (currentVisual != null && itemSearchType != null)
{
var currentVisualType = currentVisual.GetType();
if (currentVisualType == itemSearchType || currentVisualType.IsSubclassOf(itemSearchType))
{
if (currentVisual is TreeViewItem || itemsControl.ItemContainerGenerator.IndexFromContainer(currentVisual) != -1)
{
return currentVisual;
}
}
if (itemContainerSearchType.IsAssignableFrom(currentVisualType))
{
// ok, we found an ItemsControl (maybe an empty)
return null;
}
currentVisual = VisualTreeHelper.GetParent(currentVisual);
}
return null;
}
/// <summary>
/// find the visual ancestor by type and go through the visual tree until the given itemsControl will be found
/// </summary>
public static DependencyObject GetVisualAncestor(this DependencyObject d, Type itemSearchType, [NotNull] ItemsControl itemsControl)
{
if (itemsControl == null) throw new ArgumentNullException(nameof(itemsControl));
var visualTreeRoot = d.FindVisualTreeRoot();
var currentVisual = VisualTreeHelper.GetParent(visualTreeRoot);
DependencyObject lastFoundItemByType = null;
while (currentVisual != null && itemSearchType != null)
{
if (currentVisual == itemsControl)
{
return lastFoundItemByType;
}
var currentVisualType = currentVisual.GetType();
if ((currentVisualType == itemSearchType || currentVisualType.IsSubclassOf(itemSearchType))
&& (itemsControl.ItemContainerGenerator.IndexFromContainer(currentVisual) != -1))
{
lastFoundItemByType = currentVisual;
}
currentVisual = VisualTreeHelper.GetParent(currentVisual);
}
return lastFoundItemByType;
}
public static T GetVisualDescendent<T>(this DependencyObject d)
where T : DependencyObject
{
return d.GetVisualDescendents<T>().FirstOrDefault();
}
public static IEnumerable<T> GetVisualDescendents<T>(this DependencyObject d)
where T : DependencyObject
{
var childCount = VisualTreeHelper.GetChildrenCount(d);
for (var n = 0; n < childCount; n++)
{
var child = VisualTreeHelper.GetChild(d, n);
if (child is T)
{
yield return (T)child;
}
foreach (var match in GetVisualDescendents<T>(child))
{
yield return match;
}
}
yield break;
}
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
</configuration>

View File

@@ -0,0 +1,253 @@
<Application x:Class="Showcase.WPF.DragDrop.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="TabItemPanelBackgroundBrush" Color="Silver" />
<SolidColorBrush x:Key="TabItemBackgroundIsSelectedBrush" Color="Gray" />
<SolidColorBrush x:Key="TabItemBackgroundIsMouseOverBrush" Color="#A5A5A5" />
<SolidColorBrush x:Key="TabItemForegroundIsSelectedBrush" Color="White" />
<SolidColorBrush x:Key="TabItemSelectorBrush" Color="LawnGreen" />
<Style x:Key="GitHubIssueButtonStyle"
x:Shared="False"
TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Command" Value="{Binding OpenIssueCommand}" />
<Setter Property="Content">
<Setter.Value>
<iconPacks:PackIconMaterial Width="24"
Height="24"
Kind="GithubCircle" />
</Setter.Value>
</Setter>
<Setter Property="Cursor" Value="Hand" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Margin" Value="0 1 4 1" />
<Setter Property="Padding" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Grid Background="Transparent">
<Ellipse Fill="{TemplateBinding Background}" StrokeThickness="0" />
<ContentPresenter x:Name="PART_ContentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="#4078C0" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Foreground" Value="#315E95" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
<Style x:Key="GitHubPullRequestButtonStyle"
x:Shared="False"
BasedOn="{StaticResource GitHubIssueButtonStyle}"
TargetType="{x:Type Button}">
<Setter Property="Command" Value="{Binding OpenPullRequestCommand}" />
</Style>
<Style x:Key="OpenLinkButtonStyle"
x:Shared="False"
BasedOn="{StaticResource GitHubIssueButtonStyle}"
TargetType="{x:Type Button}">
<Setter Property="Command" Value="{Binding OpenLinkCommand}" />
<Setter Property="Content">
<Setter.Value>
<iconPacks:PackIconFeatherIcons Width="20"
Height="20"
Kind="ExternalLink" />
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DefaultTabItemStyle" TargetType="{x:Type TabItem}" />
<Style x:Key="DefaultTabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Height" Value="100" />
<Setter Property="ItemContainerStyle" Value="{StaticResource DefaultTabItemStyle}" />
<Setter Property="Margin" Value="4" />
<Setter Property="TabStripPlacement" Value="Top" />
</Style>
<Style x:Key="ShowcaseTabItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="PART_Grid"
Margin="0"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<TextBlock Name="PART_Text"
Margin="0"
Padding="10"
VerticalAlignment="Center"
FontSize="14"
Text="{TemplateBinding Header}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" />
<Rectangle x:Name="PART_Selector"
Width="4"
HorizontalAlignment="Left"
Fill="{StaticResource TabItemSelectorBrush}"
Visibility="Collapsed" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.Setters>
<Setter Property="Background" Value="{StaticResource TabItemBackgroundIsSelectedBrush}" />
<Setter TargetName="PART_Text" Property="Foreground" Value="{StaticResource TabItemForegroundIsSelectedBrush}" />
<Setter TargetName="PART_Selector" Property="Visibility" Value="Visible" />
</Trigger.Setters>
</Trigger>
<Trigger SourceName="PART_Grid" Property="IsMouseOver" Value="True">
<Trigger.Setters>
<Setter Property="Background" Value="{StaticResource TabItemBackgroundIsMouseOverBrush}" />
</Trigger.Setters>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Width" Value="120" />
</Style>
<Style x:Key="ShowcaseTabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="ItemContainerStyle" Value="{StaticResource ShowcaseTabItemStyle}" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="TabStripPlacement" Value="Left" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<DockPanel LastChildFill="True">
<WrapPanel x:Name="HeaderPanel"
Panel.ZIndex="1"
Background="{StaticResource TabItemPanelBackgroundBrush}"
DockPanel.Dock="Left"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Orientation="Vertical" />
<Border x:Name="ContentPanel"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Local"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<ContentPresenter x:Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="TabStripPlacement" Value="Top">
<Setter TargetName="HeaderPanel" Property="DockPanel.Dock" Value="Top" />
<Setter TargetName="HeaderPanel" Property="Orientation" Value="Horizontal" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DefaultTextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="14" />
<Setter Property="Margin" Value="4" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<Style x:Key="SampleHeaderTextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="Gainsboro" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Foreground" Value="Gray" />
<Setter Property="Padding" Value="10" />
</Style>
<Style x:Key="DefaultListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Padding" Value="2" />
</Style>
<Style BasedOn="{StaticResource DefaultListBoxItemStyle}" TargetType="{x:Type ListBoxItem}" />
<Style x:Key="DefaultListBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="Height" Value="300" />
<Setter Property="ItemContainerStyle" Value="{StaticResource DefaultListBoxItemStyle}" />
<Setter Property="Margin" Value="4" />
<Setter Property="SelectionMode" Value="Extended" />
</Style>
<Style x:Key="DefaultItemsControl" TargetType="{x:Type ItemsControl}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="#FF888888" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Height" Value="300" />
<Setter Property="Margin" Value="4" />
</Style>
<Style x:Key="DefaultListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Padding" Value="2" />
</Style>
<Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="{x:Type ListViewItem}" />
<Style x:Key="DefaultListViewStyle" TargetType="{x:Type ListView}">
<Setter Property="Height" Value="300" />
<Setter Property="ItemContainerStyle" Value="{StaticResource DefaultListViewItemStyle}" />
<Setter Property="Margin" Value="4" />
<Setter Property="SelectionMode" Value="Extended" />
</Style>
<Style x:Key="DefaultTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Margin="2" Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Padding" Value="2" />
</Style>
<Style BasedOn="{StaticResource DefaultTreeViewItemStyle}" TargetType="{x:Type TreeViewItem}" />
<Style x:Key="DefaultTreeViewStyle" TargetType="{x:Type TreeView}">
<Setter Property="Height" Value="300" />
<Setter Property="ItemContainerStyle" Value="{StaticResource DefaultTreeViewItemStyle}" />
<Setter Property="Margin" Value="4" />
</Style>
<Style x:Key="DefaultDataGridStyle" TargetType="{x:Type DataGrid}">
<Setter Property="Height" Value="300" />
<Setter Property="HorizontalGridLinesBrush" Value="Silver" />
<Setter Property="Margin" Value="4" />
<Setter Property="SelectionMode" Value="Extended" />
<Setter Property="VerticalGridLinesBrush" Value="Silver" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,11 @@
using System.Windows;
namespace Showcase.WPF.DragDrop
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@@ -0,0 +1,26 @@
using System.Windows;
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.CustomControls
{
public class CustomListBox : ListBox
{
protected override DependencyObject GetContainerForItemOverride()
{
return new CustomListBoxItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is CustomListBoxItem;
}
}
public class CustomListBoxItem : ListBoxItem
{
public override string ToString()
{
return base.ToString();
}
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura DisableCompression="false" />
</Weavers>

View File

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -0,0 +1,43 @@
<Window x:Class="Showcase.WPF.DragDrop.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
xmlns:views="clr-namespace:Showcase.WPF.DragDrop.Views"
Title="GongSolutions.WPF.DragDrop"
Width="900"
Height="600"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid>
<TabControl x:Name="MainTabControl" Style="{StaticResource ShowcaseTabControlStyle}">
<TabItem Header="ListBox">
<views:ListBoxSamples />
</TabItem>
<TabItem Header="ListView">
<views:ListViewSamples />
</TabItem>
<TabItem Header="TreeView">
<views:TreeViewSamples />
</TabItem>
<TabItem Header="DataGrid">
<views:DataGridSamples />
</TabItem>
<TabItem Header="TabControl">
<views:TabControlSamples />
</TabItem>
<TabItem Header="Mixed">
<views:MixedSamples />
</TabItem>
<TabItem Header="Fixed #Issues">
<views:Issues />
</TabItem>
</TabControl>
</Grid>
</Window>

View File

@@ -0,0 +1,30 @@
using System;
using System.Linq;
using System.Windows.Controls;
using System.Windows;
using Showcase.WPF.DragDrop.ViewModels;
namespace Showcase.WPF.DragDrop
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
this.Loaded += MainWindowLoaded;
}
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
var appArgs = Environment.GetCommandLineArgs();
if (appArgs.Length > 1 && appArgs[1] == "anotherOne")
{
this.MainTabControl.SelectedItem = MainTabControl.Items.OfType<TabItem>().FirstOrDefault(t => (string)t.Header == "Mixed");
}
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
namespace Showcase.WPF.DragDrop.Models
{
public class ClonableItemModel : ItemModel, ICloneable
{
public ClonableItemModel()
{
}
public ClonableItemModel(int itemIndex)
: base(itemIndex)
{
}
public object Clone()
{
var clonableItemModel = new ClonableItemModel();
clonableItemModel.BindableDoubleValue = this.BindableDoubleValue;
clonableItemModel.SubItemCollection.Clear();
foreach (var subItem in this.SubItemCollection)
{
clonableItemModel.SubItemCollection.Add(subItem);
}
clonableItemModel.SelectedSubItem = this.SelectedSubItem;
clonableItemModel.Index = this.Index;
clonableItemModel.Caption = $"Cloned Item {this.Index}";
return clonableItemModel;
}
}
}

View File

@@ -0,0 +1,13 @@
using Faker;
namespace Showcase.WPF.DragDrop.Models
{
public class DataGridRowModel
{
public string Name { get; set; } = Faker.Name.FullName(NameFormats.Standard);
public string StreetName { get; set; } = Faker.Address.StreetName();
public string City { get; set; } = Faker.Address.City();
}
}

View File

@@ -0,0 +1,18 @@
using System.Windows;
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.Models
{
public class DragAdornerTemplateSelector : DataTemplateSelector
{
public DataTemplate TemplateEven { get; set; }
public DataTemplate TemplateOdd { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var itemModel = item as ItemModel;
return itemModel != null && (itemModel.Index & 0x01) == 0 ? TemplateEven : TemplateOdd;
}
}
}

View File

@@ -0,0 +1,41 @@
using System.ComponentModel;
using System.Linq;
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
/// <summary>
/// Custom drop handler which is used for the grouping example.
/// </summary>
public class GroupedDropHandler : IDropTarget
{
public void DragOver(IDropInfo dropInfo)
{
// Call default DragOver method, cause most stuff should work by default
GongSolutions.Wpf.DragDrop.DragDrop.DefaultDropHandler.DragOver(dropInfo);
if (dropInfo.TargetGroup == null)
{
dropInfo.Effects = System.Windows.DragDropEffects.None;
}
}
public void Drop(IDropInfo dropInfo)
{
// The default drop handler don't know how to set an item's group. You need to explicitly set the group on the dropped item like this.
GongSolutions.Wpf.DragDrop.DragDrop.DefaultDropHandler.Drop(dropInfo);
// Now extract the dragged group items and set the new group (target)
var data = DefaultDropHandler.ExtractData(dropInfo.Data).OfType<GroupedItem>().ToList();
foreach (var groupedItem in data)
{
groupedItem.Group = dropInfo.TargetGroup.Name.ToString();
}
// Changing group data at runtime isn't handled well: force a refresh on the collection view.
if (dropInfo.TargetCollection is ICollectionView)
{
((ICollectionView)dropInfo.TargetCollection).Refresh();
}
}
}
}

View File

@@ -0,0 +1,52 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Showcase.WPF.DragDrop.Models
{
public class GroupedItem : INotifyPropertyChanged
{
private string _caption;
private string _group;
public GroupedItem(int group, int item)
{
this.Caption = $"Item {item} from Group {group}";
this._group = $"Group {group}";
}
public string Caption
{
get { return _caption; }
set
{
if (value == _caption) return;
_caption = value;
OnPropertyChanged();
}
}
public string Group
{
get { return _group; }
set
{
if (value == _group)
{
return;
}
_group = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,155 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using JetBrains.Annotations;
using Showcase.WPF.DragDrop.ViewModels;
namespace Showcase.WPF.DragDrop.Models
{
public class ItemModel : INotifyPropertyChanged
{
private double _bindableDoubleValue;
private string _selectedSubItem;
public ItemModel()
{
this.BindableDoubleValue = Faker.RandomNumber.Next(0, 100);
for (int i = 0; i < Faker.RandomNumber.Next(2, 20); i++)
{
SubItemCollection.Add(new SubItemModel($"Sub item {i}"));
}
}
public ItemModel(int itemIndex)
: this()
{
this.Index = itemIndex;
this.Caption = $"Item {itemIndex}";
}
public int Index { get; set; }
public string Caption { get; set; }
public ObservableCollection<SubItemModel> SubItemCollection { get; set; } = new ObservableCollection<SubItemModel>();
public string SelectedSubItem
{
get { return _selectedSubItem; }
set
{
if (value == _selectedSubItem) return;
_selectedSubItem = value;
OnPropertyChanged();
}
}
public double BindableDoubleValue
{
get { return _bindableDoubleValue; }
set
{
if (value.Equals(_bindableDoubleValue)) return;
_bindableDoubleValue = value;
OnPropertyChanged();
}
}
public override string ToString()
{
return this.Caption;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SubItemModel : INotifyPropertyChanged
{
private string _bindableValue;
private bool _bindableOptionA;
private bool _bindableOptionB;
public SubItemModel(string caption)
{
this.Caption = caption;
this.ButtonTestCommand = new SimpleCommand(o => { this.BindableValue = $"Button clicked at {DateTime.UtcNow.ToLocalTime()}"; });
}
public string Caption { get; set; }
public ICommand ButtonTestCommand { get; set; }
public string BindableValue
{
get { return _bindableValue; }
set
{
if (value == _bindableValue) return;
_bindableValue = value;
OnPropertyChanged();
}
}
public bool BindableOptionA
{
get { return _bindableOptionA; }
set
{
if (value == _bindableOptionA) return;
_bindableOptionA = value;
OnPropertyChanged();
}
}
public bool BindableOptionB
{
get { return _bindableOptionB; }
set
{
if (value == _bindableOptionB) return;
_bindableOptionB = value;
OnPropertyChanged();
}
}
public override string ToString()
{
return this.Caption;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
[Serializable]
public class SerializableItemModel
{
public SerializableItemModel(int itemIndex)
{
this.Index = itemIndex;
this.Caption = $"{itemIndex}. Item";
}
public int Index { get; set; }
public string Caption { get; set; }
public override string ToString()
{
return this.Caption;
}
}
}

View File

@@ -0,0 +1,31 @@
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
public class ListBoxCustomDropHandler : DefaultDropHandler
{
public override void DragOver(IDropInfo dropInfo)
{
if (dropInfo.VisualTarget == dropInfo.DragInfo.VisualSource)
{
dropInfo.NotHandled = dropInfo.VisualTarget == dropInfo.DragInfo.VisualSource;
}
else
{
base.DragOver(dropInfo);
}
}
public override void Drop(IDropInfo dropInfo)
{
if (dropInfo.VisualTarget == dropInfo.DragInfo.VisualSource)
{
dropInfo.NotHandled = dropInfo.VisualTarget == dropInfo.DragInfo.VisualSource;
}
else
{
base.Drop(dropInfo);
}
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Windows;
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
public class NestedDropHandler : IDropTarget
{
public void DragOver(IDropInfo dropInfo)
{
if (dropInfo.TargetItem?.ToString().StartsWith("Root", StringComparison.OrdinalIgnoreCase) == true)
{
dropInfo.Effects = DragDropEffects.Move;
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
}
}
public void Drop(IDropInfo dropInfo)
{
// nothing
}
}
}

View File

@@ -0,0 +1,105 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
public class SampleData
{
public SampleData()
{
for (var n = 0; n < 50; ++n)
{
this.SerializableCollection1.Add(new SerializableItemModel(n + 1));
this.Collection1.Add(new ItemModel(n + 1));
this.FilterCollection1.Add(new ItemModel(n + 1));
this.ClonableCollection1.Add(new ClonableItemModel(n + 1));
this.DataGridCollection1.Add(new DataGridRowModel());
}
for (var n = 0; n < 10; ++n)
{
this.Collection4.Add(new ItemModel() { Caption = $"Model {n + 1}" });
}
for (var g = 0; g < 4; ++g)
{
for (var i = 0; i < ((g % 2) == 0 ? 4 : 2); ++i)
{
this.GroupedCollection.Add(new GroupedItem(g, i));
}
}
this.GroupedItemsCollectionViewSource = CollectionViewSource.GetDefaultView(this.GroupedCollection);
this.GroupedItemsCollectionViewSource.GroupDescriptions.Add(new PropertyGroupDescription() { PropertyName = nameof(GroupedItem.Group) });
for (int r = 1; r <= 6; r++)
{
var root = new TreeNode($"Root {r}");
for (var i = 0; i < ((r % 2) == 0 ? 8 : 3); ++i)
{
root.Children.Add(new TreeNode($"Item {i + 10 * r}"));
}
this.TreeCollection1.Add(root);
if (r == 2)
{
root.IsExpanded = true;
}
}
for (int i = 0; i < 5; i++)
{
this.TabItemCollection1.Add(new TabItemModel(i + 1));
}
this.TabItemCollection2.Add(new TabItemModel(1));
}
public ObservableCollection<SerializableItemModel> SerializableCollection1 { get; set; } = new ObservableCollection<SerializableItemModel>();
public ObservableCollection<SerializableItemModel> SerializableCollection2 { get; set; } = new ObservableCollection<SerializableItemModel>();
public SerializableDragHandler SerializableDragHandler { get; set; } = new SerializableDragHandler();
public SerializableDropHandler SerializableDropHandler { get; set; } = new SerializableDropHandler();
public ObservableCollection<ItemModel> Collection1 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ItemModel> Collection2 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ItemModel> Collection3 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ItemModel> Collection4 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ClonableItemModel> ClonableCollection1 { get; set; } = new ObservableCollection<ClonableItemModel>();
public ObservableCollection<ClonableItemModel> ClonableCollection2 { get; set; } = new ObservableCollection<ClonableItemModel>();
public ObservableCollection<ItemModel> FilterCollection1 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<ItemModel> FilterCollection2 { get; set; } = new ObservableCollection<ItemModel>();
public ObservableCollection<TreeNode> TreeCollection1 { get; set; } = new ObservableCollection<TreeNode>();
public ObservableCollection<TreeNode> TreeCollection2 { get; set; } = new ObservableCollection<TreeNode>();
public GroupedDropHandler GroupedDropHandler { get; set; } = new GroupedDropHandler();
public ObservableCollection<GroupedItem> GroupedCollection { get; set; } = new ObservableCollection<GroupedItem>();
public ICollectionView GroupedItemsCollectionViewSource { get; }
public ObservableCollection<DataGridRowModel> DataGridCollection1 { get; set; } = new ObservableCollection<DataGridRowModel>();
public ObservableCollection<DataGridRowModel> DataGridCollection2 { get; set; } = new ObservableCollection<DataGridRowModel>();
public ObservableCollection<TabItemModel> TabItemCollection1 { get; set; } = new ObservableCollection<TabItemModel>();
public ObservableCollection<TabItemModel> TabItemCollection2 { get; set; } = new ObservableCollection<TabItemModel>();
public TextBoxCustomDropHandler TextBoxCustomDropHandler { get; set; } = new TextBoxCustomDropHandler();
public ListBoxCustomDropHandler ListBoxCustomDropHandler { get; set; } = new ListBoxCustomDropHandler();
public IDropTarget NestedDropHandler { get; set; } = new NestedDropHandler();
}
}

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using GongSolutions.Wpf.DragDrop;
using GongSolutions.Wpf.DragDrop.Utilities;
namespace Showcase.WPF.DragDrop.Models
{
[Serializable]
public class SerializableWrapper
{
public IEnumerable<object> Items { get; set; }
public DragDropKeyStates DragDropCopyKeyState { get; set; }
}
public class SerializableDragHandler : IDragSource
{
private bool alreadyDropped = false;
public void StartDrag(IDragInfo dragInfo)
{
alreadyDropped = false;
var items = dragInfo.SourceItems.OfType<object>().ToList();
var wrapper = new SerializableWrapper()
{
Items = items,
DragDropCopyKeyState = DragDropKeyStates.ControlKey //dragInfo.DragDropCopyKeyState
};
dragInfo.Data = wrapper;
dragInfo.DataFormat = DataFormats.GetDataFormat(DataFormats.Serializable);
dragInfo.Effects = dragInfo.Data != null ? DragDropEffects.Copy | DragDropEffects.Move : DragDropEffects.None;
}
public bool CanStartDrag(IDragInfo dragInfo)
{
return true;
}
public void Dropped(IDropInfo dropInfo)
{
alreadyDropped = true;
}
public void DragDropOperationFinished(DragDropEffects operationResult, IDragInfo dragInfo)
{
if (alreadyDropped || dragInfo == null)
{
return;
}
// the drag operation has finished on another app
if (operationResult != DragDropEffects.None)
{
if (operationResult.HasFlag(DragDropEffects.Move))
{
var sourceList = dragInfo.SourceCollection.TryGetList();
var items = dragInfo.SourceItems.OfType<object>().ToList();
if (sourceList != null)
{
foreach (var o in items)
{
sourceList.Remove(o);
}
}
alreadyDropped = true;
}
}
}
public void DragCancelled()
{
}
public bool TryCatchOccurredException(Exception exception)
{
return false;
}
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Windows;
using GongSolutions.Wpf.DragDrop;
using GongSolutions.Wpf.DragDrop.Utilities;
namespace Showcase.WPF.DragDrop.Models
{
public class SerializableDropHandler : IDropTarget
{
public void DragOver(IDropInfo dropInfo)
{
var wrapper = GetSerializableWrapper(dropInfo);
if (wrapper != null && dropInfo.TargetCollection != null)
{
dropInfo.Effects = ShouldCopyData(dropInfo, wrapper.DragDropCopyKeyState) ? DragDropEffects.Copy : DragDropEffects.Move;
dropInfo.DropTargetAdorner = DropTargetAdorners.Insert;
}
}
public void Drop(IDropInfo dropInfo)
{
var wrapper = GetSerializableWrapper(dropInfo);
if (wrapper != null && dropInfo.TargetCollection != null)
{
// at this point the drag info can be null, cause the other app doesn't know it
var insertIndex = dropInfo.InsertIndex != dropInfo.UnfilteredInsertIndex ? dropInfo.UnfilteredInsertIndex : dropInfo.InsertIndex;
var destinationList = dropInfo.TargetCollection.TryGetList();
var copyData = ShouldCopyData(dropInfo, wrapper.DragDropCopyKeyState);
if (!copyData)
{
var sourceList = dropInfo.DragInfo?.SourceCollection?.TryGetList();
if (sourceList != null)
{
foreach (var o in wrapper.Items)
{
var index = sourceList.IndexOf(o);
if (index != -1)
{
sourceList.RemoveAt(index);
// so, is the source list the destination list too ?
if (destinationList != null && Equals(sourceList, destinationList) && index < insertIndex)
{
--insertIndex;
}
}
}
}
}
if (destinationList != null)
{
// check for cloning
var cloneData = dropInfo.Effects.HasFlag(DragDropEffects.Copy)
|| dropInfo.Effects.HasFlag(DragDropEffects.Link);
foreach (var o in wrapper.Items)
{
var obj2Insert = o;
if (cloneData)
{
var cloneable = o as ICloneable;
if (cloneable != null)
{
obj2Insert = cloneable.Clone();
}
}
destinationList.Insert(insertIndex++, obj2Insert);
}
}
}
}
private static SerializableWrapper GetSerializableWrapper(IDropInfo dropInfo)
{
var data = dropInfo.Data;
var dataObject = data as DataObject;
if (dataObject != null)
{
var dataFormat = DataFormats.GetDataFormat(DataFormats.Serializable);
data = dataObject.GetDataPresent(dataFormat.Name) ? dataObject.GetData(dataFormat.Name) : data;
}
var wrapper = data as SerializableWrapper;
return wrapper;
}
private static bool ShouldCopyData(IDropInfo dropInfo, DragDropKeyStates dragDropCopyKeyState)
{
// default should always the move action/effect
if (dropInfo == null)
{
return false;
}
var copyData = ((dragDropCopyKeyState != default(DragDropKeyStates)) && dropInfo.KeyStates.HasFlag(dragDropCopyKeyState))
|| dragDropCopyKeyState.HasFlag(DragDropKeyStates.LeftMouseButton);
return copyData;
}
}
}

View File

@@ -0,0 +1,15 @@
namespace Showcase.WPF.DragDrop.Models
{
public class TabItemModel
{
public TabItemModel(int itemIndex)
{
this.Header = $"TabItem {itemIndex}";
this.Content = Faker.Lorem.Paragraph();
}
public string Header { get; set; }
public string Content { get; set; }
}
}

View File

@@ -0,0 +1,53 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using GongSolutions.Wpf.DragDrop;
namespace Showcase.WPF.DragDrop.Models
{
public class TextBoxCustomDropHandler : IDropTarget
{
public void DragOver(IDropInfo dropInfo)
{
dropInfo.DropTargetAdorner = typeof(DropTargetHighlightAdorner);
dropInfo.Effects = DragDropEffects.Move;
}
public void Drop(IDropInfo dropInfo)
{
var dataAsList = DefaultDropHandler.ExtractData(dropInfo.Data);
((TextBox)dropInfo.VisualTarget).Text = string.Join(", ", dataAsList.OfType<object>().ToArray());
}
}
public class DropTargetHighlightAdorner : DropTargetAdorner
{
private readonly Pen _pen;
private readonly Brush _brush;
public DropTargetHighlightAdorner(UIElement adornedElement, DropInfo dropInfo)
: base(adornedElement, dropInfo)
{
_pen = new Pen(Brushes.Tomato, 2);
_pen.Freeze();
_brush = new SolidColorBrush(Colors.Coral) { Opacity = 0.2 };
this._brush.Freeze();
this.SetValue(SnapsToDevicePixelsProperty, true);
}
protected override void OnRender(DrawingContext drawingContext)
{
var visualTarget = this.DropInfo.VisualTarget;
if (visualTarget != null)
{
var translatePoint = visualTarget.TranslatePoint(new Point(), this.AdornedElement);
translatePoint.Offset(1, 1);
var bounds = new Rect(translatePoint,
new Size(visualTarget.RenderSize.Width - 2, visualTarget.RenderSize.Height - 2));
drawingContext.DrawRectangle(_brush, _pen, bounds);
}
}
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Showcase.WPF.DragDrop.Models
{
public class TreeNode : INotifyPropertyChanged, ICloneable
{
private string _caption;
private ObservableCollection<TreeNode> _children;
private bool _isCloned;
private bool _isExpanded;
public TreeNode(string caption)
{
this.Caption = caption;
this.Children = new ObservableCollection<TreeNode>();
}
public string Caption
{
get { return _caption; }
set
{
if (value == _caption) return;
_caption = value;
OnPropertyChanged();
}
}
public ObservableCollection<TreeNode> Children
{
get { return _children; }
set
{
if (Equals(value, _children)) return;
_children = value;
OnPropertyChanged();
}
}
public bool IsCloned
{
get { return _isCloned; }
set
{
if (value == _isCloned) return;
_isCloned = value;
OnPropertyChanged();
}
}
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (value == _isExpanded) return;
_isExpanded = value;
OnPropertyChanged();
}
}
public override string ToString()
{
return this.Caption;
}
public object Clone()
{
var treeNode = new TreeNode(this.Caption) { IsCloned = true };
foreach (var child in this.Children)
{
treeNode.Children.Add((TreeNode)child.Clone());
}
return treeNode;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,6 @@
using System.Runtime.InteropServices;
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<!-- Project properties -->
<PropertyGroup>
<TargetFrameworks>net45;net46;netcoreapp3.0</TargetFrameworks>
<AssemblyName>Showcase.WPF.DragDrop</AssemblyName>
<RootNamespace>Showcase.WPF.DragDrop</RootNamespace>
<DisableFody Condition="'$(Configuration)' == 'Debug' or '$(TargetFramework)' == 'netcoreapp3.0'">true</DisableFody>
<Description>Showcase application for GongSolutions.WPF.DragDrop</Description>
</PropertyGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<NoWarn>SA1652</NoWarn>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<!-- reference includes -->
<ItemGroup>
<PackageReference Include="Fody" Version="6.0.0" PrivateAssets="All" />
<PackageReference Include="Costura.Fody" Version="4.*" PrivateAssets="All" />
<PackageReference Include="MahApps.Metro.IconPacks" Version="3.0.0-alpha*" />
<PackageReference Include="Faker.Net" Version="1.*" />
</ItemGroup>
<ItemGroup>
<None Include="app.manifest" />
<None Remove="App.config" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp3.0'">
<AppConfigWithTargetPath Include="App.$(TargetFramework).config">
<TargetPath>$(AssemblyName).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GongSolutions.WPF.DragDrop\GongSolutions.WPF.DragDrop.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,132 @@
using System.Diagnostics;
using System.Windows.Data;
using System.Windows.Input;
using Showcase.WPF.DragDrop.Models;
namespace Showcase.WPF.DragDrop.ViewModels
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class ListMember : List<SubMember>
{
public string Name { get; set; }
}
public class SubMember
{
public string Name { get; set; }
}
public class MainViewModel : ViewModelBase
{
private SampleData _data;
private ICommand _openIssueCommand;
private ICommand _openPullRequestCommand;
private ICommand _openLinkCommand;
private ICommand _filterCollectionCommand;
public ObservableCollection<ListMember> Members { get; set; } = new ObservableCollection<ListMember>();
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
////if (IsInDesignMode)
////{
//// // Code runs in Blend --> create design time data.
////}
////else
////{
//// // Code runs "for real"
////}
var listMember = new ListMember() { Name = "Item 1" };
Members.Add(listMember);
listMember = new ListMember() { Name = "Item 2 with SubItems" };
listMember.Add(new SubMember() { Name = "SubItem 1" });
listMember.Add(new SubMember() { Name = "SubItem 2" });
Members.Add(listMember);
listMember = new ListMember() { Name = "Item 3" };
Members.Add(listMember);
this.Data = new SampleData();
this.OpenIssueCommand = new SimpleCommand(issue => { Process.Start($"https://github.com/punker76/gong-wpf-dragdrop/issues/{issue}"); });
this.OpenPullRequestCommand = new SimpleCommand(pr => { Process.Start($"https://github.com/punker76/gong-wpf-dragdrop/pull/{pr}"); });
this.OpenLinkCommand = new SimpleCommand(link => { Process.Start(link.ToString()); });
this.FilterCollectionCommand = new SimpleCommand(isChecked =>
{
var coll = Data.FilterCollection1;
var collView = CollectionViewSource.GetDefaultView(coll);
collView.Filter += o =>
{
if (!(isChecked as bool?).GetValueOrDefault())
{
return true;
}
var itemModel = (ItemModel)o;
var number = itemModel.Index;
return (number & 0x01) == 0;
};
});
}
public SampleData Data
{
get { return _data; }
set
{
if (Equals(value, _data)) return;
_data = value;
OnPropertyChanged();
}
}
public ICommand OpenIssueCommand
{
get { return _openIssueCommand; }
set
{
if (Equals(value, _openIssueCommand)) return;
_openIssueCommand = value;
OnPropertyChanged();
}
}
public ICommand OpenPullRequestCommand
{
get { return _openPullRequestCommand; }
set
{
if (Equals(value, _openPullRequestCommand)) return;
_openPullRequestCommand = value;
OnPropertyChanged();
}
}
public ICommand OpenLinkCommand
{
get { return _openLinkCommand; }
set
{
if (Equals(value, _openLinkCommand)) return;
_openLinkCommand = value;
OnPropertyChanged();
}
}
public ICommand FilterCollectionCommand
{
get { return _filterCollectionCommand; }
set
{
if (Equals(value, _filterCollectionCommand)) return;
_filterCollectionCommand = value;
OnPropertyChanged();
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Windows.Input;
namespace Showcase.WPF.DragDrop.ViewModels
{
public class SimpleCommand : ICommand
{
public SimpleCommand(Action<object> execute = null, Predicate<object> canExecute = null)
{
this.CanExecuteDelegate = canExecute;
this.ExecuteDelegate = execute;
}
public Predicate<object> CanExecuteDelegate { get; set; }
public Action<object> ExecuteDelegate { get; set; }
public bool CanExecute(object parameter)
{
var canExecute = this.CanExecuteDelegate;
return canExecute == null || canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
this.ExecuteDelegate?.Invoke(parameter);
}
}
}

View File

@@ -0,0 +1,17 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Showcase.WPF.DragDrop.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,65 @@
<UserControl x:Class="Showcase.WPF.DragDrop.Views.DataGridSamples"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="urn:gong-wpf-dragdrop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Showcase.WPF.DragDrop.ViewModels"
d:DataContext="{d:DesignInstance viewModels:MainViewModel}"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style BasedOn="{StaticResource DefaultDataGridStyle}" TargetType="{x:Type DataGrid}" />
</Grid.Resources>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top"
Style="{StaticResource SampleHeaderTextBlockStyle}"
Text="DataGrid" />
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Demonstrates the default behaviour with two DataGrids bound to two collections of the same type. Just try drag and drop some Rows from left DataGrid to right DataGrid." />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<DataGrid x:Name="LeftBoundDataGrid"
Grid.Column="0"
dd:DragDrop.DropScrollingMode="VerticalOnly"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.ShowAlwaysDropTargetAdorner="True"
dd:DragDrop.UseDefaultEffectDataTemplate="True"
CanUserAddRows="False"
CanUserDeleteRows="False"
IsReadOnly="True"
ItemsSource="{Binding Data.DataGridCollection1}" />
<DataGrid Grid.Column="1"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
CanUserAddRows="True"
CanUserDeleteRows="True"
ItemsSource="{Binding Data.DataGridCollection2}" />
</Grid>
<TextBlock Style="{StaticResource DefaultTextBlockStyle}" Text="Customization (for left DataGrid)" />
<CheckBox Margin="10 5"
Content="IsDragSource"
IsChecked="{Binding ElementName=LeftBoundDataGrid, Path=(dd:DragDrop.IsDragSource), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="IsDropTarget"
IsChecked="{Binding ElementName=LeftBoundDataGrid, Path=(dd:DragDrop.IsDropTarget), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultDragAdorner"
IsChecked="{Binding ElementName=LeftBoundDataGrid, Path=(dd:DragDrop.UseDefaultDragAdorner), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Margin="10 5"
Content="UseDefaultEffectDataTemplate"
IsChecked="{Binding ElementName=LeftBoundDataGrid, Path=(dd:DragDrop.UseDefaultEffectDataTemplate), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Showcase.WPF.DragDrop.Views
{
/// <summary>
/// Interaction logic for DataGridSamples.xaml
/// </summary>
public partial class DataGridSamples : UserControl
{
public DataGridSamples()
{
InitializeComponent();
}
}
}

Some files were not shown because too many files have changed in this diff Show More