Initial commit

This commit is contained in:
2014-04-30 17:33:21 -04:00
commit f965f46fb3
33 changed files with 2949 additions and 0 deletions

50
CheckedListItem.cs Normal file
View File

@@ -0,0 +1,50 @@
using System.ComponentModel;
namespace Common.Wpf
{
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isChecked;
private T _item;
public CheckedListItem() { }
public CheckedListItem(T item, bool isChecked = false)
{
_item = item;
_isChecked = isChecked;
}
public T Item
{
get
{
return _item;
}
set
{
_item = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
}

245
Common.Wpf.csproj Normal file
View File

@@ -0,0 +1,245 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{0074C983-550E-4094-9E8C-F566FB669297}</ProjectGuid>
<OutputType>library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Common.Wpf</RootNamespace>
<AssemblyName>Common.Wpf</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\ChrisKaczor.pfx</AssemblyOriginatorKeyFile>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<CodeAnalysisLogFile>bin\Debug\Common.Wpf.dll.CodeAnalysisLog.xml</CodeAnalysisLogFile>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
<CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<CodeAnalysisLogFile>bin\Release\Common.Wpf.dll.CodeAnalysisLog.xml</CodeAnalysisLogFile>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisLogFile>bin\Debug\Common.Wpf.dll.CodeAnalysisLog.xml</CodeAnalysisLogFile>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<CodeAnalysisLogFile>bin\Release\Common.Wpf.dll.CodeAnalysisLog.xml</CodeAnalysisLogFile>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
<CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationFramework.Luna" />
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="PresentationCore">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="PresentationFramework">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CheckedListItem.cs" />
<Compile Include="ExtendedListBoxControl\ExtendedListBox.cs" />
<Compile Include="ExtendedListBoxControl\ExtendedListBoxItem.cs" />
<Compile Include="Extensions\ApplicationExtensions.cs" />
<Compile Include="Extensions\DependencyObjectExtensions.cs" />
<Compile Include="Extensions\FontExtensions.cs" />
<Compile Include="Extensions\GridExtensions.cs" />
<Compile Include="Extensions\WindowExtensions.cs" />
<Compile Include="HtmlLabelControl\HtmlLabel.xaml.cs">
<DependentUpon>HtmlLabel.xaml</DependentUpon>
</Compile>
<Compile Include="HtmlLabelControl\TextLine.cs" />
<Compile Include="HtmlTextBlock\HtmlTextBlock.cs" />
<Compile Include="HtmlTextBlock\TextFragment.cs" />
<Compile Include="HtmlTextBlock\TextFragmentStyle.cs" />
<Compile Include="HtmlTextBlock\TextLine.cs" />
<Compile Include="HtmlTextBlock\TextParser.cs" />
<Compile Include="LinkControl\LinkControl.xaml.cs">
<DependentUpon>LinkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="HtmlLabelControl\TextFragment.cs" />
<Compile Include="HtmlLabelControl\TextFragmentStyle.cs" />
<Compile Include="HtmlLabelControl\TextParser.cs" />
<AppDesigner Include="Properties\" />
<Compile Include="TextListControl\TextList.xaml.cs">
<DependentUpon>TextList.xaml</DependentUpon>
</Compile>
<Compile Include="Toolbar\DropDownButton\DropDownButton.cs" />
<Compile Include="Toolbar\ImageButton.xaml.cs">
<DependentUpon>ImageButton.xaml</DependentUpon>
</Compile>
<Compile Include="Toolbar\SplitButton\SplitButton.cs" />
<Compile Include="Windows\ControlBox.cs" />
<Compile Include="Windows\SnappingWindow.cs" />
</ItemGroup>
<ItemGroup>
<Page Include="HtmlLabelControl\HtmlLabel.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="LinkControl\LinkControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="TextListControl\TextList.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Toolbar\DropDownButton\DropDownButtonStyle.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Toolbar\ImageButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Toolbar\SplitButton\SplitButtonStyle.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common.Native\Common.Native.csproj">
<Project>{ed1c07a1-54f5-4796-8b06-2a0bb1960d84}</Project>
<Name>Common.Native</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,32 @@
using System.Windows;
using System.Windows.Controls;
namespace Common.Wpf.ExtendedListBoxControl
{
public class ExtendedListBox : ListBox
{
protected override DependencyObject GetContainerForItemOverride()
{
return new ExtendedListBoxItem();
}
private int _lastSelectedIndex = -1;
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
if (SelectedIndex == -1 && _lastSelectedIndex != -1)
{
int itemCount = Items.Count;
if (_lastSelectedIndex >= itemCount)
_lastSelectedIndex = itemCount - 1;
SelectedIndex = _lastSelectedIndex;
}
_lastSelectedIndex = SelectedIndex;
}
}
}

View File

@@ -0,0 +1,75 @@
using System.Windows.Controls;
using System.Windows.Input;
using Common.Wpf.Extensions;
namespace Common.Wpf.ExtendedListBoxControl
{
public class ExtendedListBoxItem : ListBoxItem
{
#region Helper properties
public ExtendedListBox ParentListBox
{
get
{
return this.GetAncestor<ExtendedListBox>();
}
}
#endregion
#region Fix selection handling for multiple selection
private bool _fireMouseDownOnMouseUp;
private SelectionMode? _selectionMode;
private SelectionMode SelectionMode
{
get
{
if (_selectionMode == null)
{
// Get the parent list box
ListBox listBox = ParentListBox;
// Cache the selection mode
_selectionMode = listBox.SelectionMode;
}
return _selectionMode.Value;
}
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
// If the item is already selected we want to ignore the mouse down now and raise it on mouse up instead
if (SelectionMode != SelectionMode.Single && IsSelected)
{
_fireMouseDownOnMouseUp = true;
return;
}
// Call the normal mouse down
base.OnMouseLeftButtonDown(e);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
// If we ignored the earlier mouse down we need to fire it now
if (SelectionMode != SelectionMode.Single && _fireMouseDownOnMouseUp)
{
// Call the normal mouse down
base.OnMouseLeftButtonDown(e);
// Clear the flag
_fireMouseDownOnMouseUp = false;
}
// Call the normal mouse up
base.OnMouseLeftButtonUp(e);
}
#endregion
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.IO;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
using System.Deployment.Application;
using Microsoft.Win32;
namespace Common.Wpf.Extensions
{
public enum HostType
{
HostTypeDefault = 0x0,
HostTypeAppLaunch = 0x1,
HostTypeCorFlag = 0x2
}
// Taken from System.Windows.Forms.UnsafeNativeMethods
[StructLayout(LayoutKind.Sequential), SuppressUnmanagedCodeSecurity]
internal class ProcessInformation
{
public IntPtr hProcess = IntPtr.Zero;
public IntPtr hThread = IntPtr.Zero;
public int dwProcessId;
public int dwThreadId;
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
~ProcessInformation()
{
close();
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
internal void close()
{
if ((hProcess != IntPtr.Zero) && (hProcess != INVALID_HANDLE_VALUE))
{
CloseHandle(new HandleRef(this, hProcess));
hProcess = INVALID_HANDLE_VALUE;
}
if ((hThread != IntPtr.Zero) && (hThread != INVALID_HANDLE_VALUE))
{
CloseHandle(new HandleRef(this, hThread));
hThread = INVALID_HANDLE_VALUE;
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
private static extern bool CloseHandle(HandleRef handle);
}
public static class ApplicationExtensions
{
[DllImport("clr.dll", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)]
internal static extern void CorLaunchApplication(uint hostType, string applicationFullName, int manifestPathsCount, string[] manifestPaths, int activationDataCount, string[] activationData, ProcessInformation processInformation);
// Originally from System.Windows.Forms.Application, changed to suit needs
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode), SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void Restart(this System.Windows.Application application)
{
if (Assembly.GetEntryAssembly() == null)
{
throw new NotSupportedException("RestartNotSupported");
}
if (ApplicationDeployment.IsNetworkDeployed)
{
string updatedApplicationFullName = ApplicationDeployment.CurrentDeployment.UpdatedApplicationFullName;
if (System.Windows.Application.Current != null)
System.Windows.Application.Current.Shutdown();
CorLaunchApplication((uint) HostType.HostTypeDefault, updatedApplicationFullName, 0, null, 0, null, new ProcessInformation());
}
}
public static void SetStartWithWindows(this System.Windows.Application application, bool value)
{
string applicationName = Assembly.GetEntryAssembly().GetName().Name;
SetStartWithWindows(application, applicationName, value);
}
public static void SetStartWithWindows(this System.Windows.Application application, string applicationName, bool value)
{
string applicationPath = string.Format("\"{0}\"", Assembly.GetEntryAssembly().Location);
SetStartWithWindows(application, applicationName, applicationPath, value);
}
public static void SetStartWithWindows(this System.Windows.Application application, string applicationName, string applicationPath, bool value)
{
// Open the regsitry key
RegistryKey regkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
// If we couldn't get the key then stop
if (regkey == null)
return;
// Delete any existing key
regkey.DeleteValue(applicationName, false);
// If auto start should not be on then we're done
if (!value)
return;
// Set the registry key
regkey.SetValue(applicationName, applicationPath);
}
}
}

View File

@@ -0,0 +1,20 @@
using System.Windows;
using System.Windows.Media;
namespace Common.Wpf.Extensions
{
public static class DependencyObjectExtensions
{
public static T GetAncestor<T>(this DependencyObject referenceObject) where T : class
{
DependencyObject parent = VisualTreeHelper.GetParent(referenceObject);
while (parent != null && !parent.GetType().Equals(typeof(T)))
{
parent = VisualTreeHelper.GetParent(parent);
}
return parent as T;
}
}
}

View File

@@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using Microsoft.Win32;
namespace Common.Wpf.Extensions
{
public static class FontExtensions
{
public static bool IsComposite(FontFamily fontFamily)
{
return fontFamily.Source.StartsWith("Global");
}
public static bool IsSymbol(FontFamily fontFamily)
{
Typeface typeface = fontFamily.GetTypefaces().First();
GlyphTypeface glyph;
typeface.TryGetGlyphTypeface(out glyph);
return glyph.Symbol;
}
public static bool IsVisible(FontFamily fontFamily)
{
return !IsHidden(fontFamily);
}
public static bool IsHidden(FontFamily fontFamily)
{
const string fontManagementKey = @"Software\Microsoft\Windows NT\CurrentVersion\Font Management";
const string inactiveFontsValue = "Inactive Fonts";
RegistryKey key = Registry.CurrentUser.OpenSubKey(fontManagementKey);
if (key == null)
return false;
IEnumerable<string> hiddenFonts = (string[]) key.GetValue(inactiveFontsValue);
return hiddenFonts.Contains(fontFamily.Source);
}
}
}

View File

@@ -0,0 +1,18 @@
using System.Windows.Controls;
namespace Common.Wpf.Extensions
{
public static class GridExtensions
{
public static int GetRowIndex(this Grid grid, RowDefinition rowDefinition)
{
for (int i = 0; i < grid.RowDefinitions.Count; i++)
{
if (grid.RowDefinitions[i] == rowDefinition)
return i;
}
return -1;
}
}
}

View File

@@ -0,0 +1,103 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace Common.Wpf.Extensions
{
public static class WindowExtensions
{
public static Dictionary<FrameworkElement, BindingExpression> GetExplicitBindingExpressions(this DependencyObject parent)
{
// Create a dictionary of framework elements and binding expressions
Dictionary<FrameworkElement, BindingExpression> bindingExpressions = new Dictionary<FrameworkElement, BindingExpression>();
// Get all explict bindings into the list
GetExplicitBindingExpressions(parent, ref bindingExpressions);
return bindingExpressions;
}
private static void GetExplicitBindingExpressions(DependencyObject parent, ref Dictionary<FrameworkElement, BindingExpression> bindingExpressions)
{
// Get the number of children
int childCount = VisualTreeHelper.GetChildrenCount(parent);
// Loop over each child
for (int childIndex = 0; childIndex < childCount; childIndex++)
{
// Get the child
DependencyObject dependencyObject = VisualTreeHelper.GetChild(parent, childIndex);
// Check if the object is a tab control
if (dependencyObject is TabControl)
{
// Cast the tab control
TabControl tabControl = (dependencyObject as TabControl);
// Loop over each tab
foreach (TabItem tabItem in tabControl.Items)
GetExplicitBindingExpressions((DependencyObject) tabItem.Content, ref bindingExpressions);
}
else
{
// See if the child is a framework element
if (dependencyObject is FrameworkElement)
{
// Cast to framework element
FrameworkElement frameworkElement = (FrameworkElement) dependencyObject;
// Get the list of properties
IEnumerable<DependencyProperty> dependencyProperties = (from PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(dependencyObject)
select DependencyPropertyDescriptor.FromProperty(propertyDescriptor)
into dependencyPropertyDescriptor
where dependencyPropertyDescriptor != null
select dependencyPropertyDescriptor.DependencyProperty).ToList();
// Loop over each dependency property in the list
foreach (DependencyProperty dependencyProperty in dependencyProperties)
{
// Try to get the binding expression for the property
BindingExpression bindingExpression = frameworkElement.GetBindingExpression(dependencyProperty);
// If there is a binding expression and it is set to explicit then make it update the source
if (bindingExpression != null && bindingExpression.ParentBinding.UpdateSourceTrigger == UpdateSourceTrigger.Explicit)
bindingExpressions.Add(frameworkElement, bindingExpression);
}
}
// If the dependency object has any children then check them
if (VisualTreeHelper.GetChildrenCount(dependencyObject) > 0)
GetExplicitBindingExpressions(dependencyObject, ref bindingExpressions);
}
}
}
public static void UpdateAllSources(this DependencyObject window)
{
UpdateAllSources(window, GetExplicitBindingExpressions(window).Values);
}
public static void UpdateAllSources(this DependencyObject window, IEnumerable<BindingExpression> bindingExpressions)
{
foreach (var expression in bindingExpressions)
expression.UpdateSource();
}
public static void ClearAllValidationErrors(this DependencyObject window)
{
ClearAllValidationErrors(window, GetExplicitBindingExpressions(window).Values);
}
public static void ClearAllValidationErrors(this DependencyObject window, IEnumerable<BindingExpression> bindingExpressions)
{
foreach (var expression in bindingExpressions)
Validation.ClearInvalid(expression);
}
}
}

View File

@@ -0,0 +1,7 @@
<UserControl x:Class="Common.Wpf.HtmlLabelControl.HtmlLabel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,122 @@
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Common.Wpf.HtmlLabelControl
{
/// <summary>
/// Interaction logic for HtmlLabel.xaml
/// </summary>
public partial class HtmlLabel : UserControl
{
private Collection<TextLine> _textLines;
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(HtmlLabel), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender));
public HtmlLabel()
{
InitializeComponent();
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
switch (e.Property.Name)
{
case "FontFamily":
case "FontSize":
case "Foreground":
// Force the control to re-parse
Text = Text;
break;
case "HorizontalContentAlignment":
case "VerticalContentAlignment":
case "Padding":
InvalidateVisual();
break;
}
}
protected override void OnRender(DrawingContext drawingContext)
{
if (string.IsNullOrEmpty(Text))
return;
Point drawPoint = new Point(Padding.Left, Padding.Top);
switch (VerticalContentAlignment)
{
case VerticalAlignment.Bottom:
drawPoint.Y = ActualHeight - Padding.Bottom;
foreach (TextLine line in _textLines)
{
drawPoint.Y -= line.Height;
}
break;
case VerticalAlignment.Center:
double totalHeight = 0;
foreach (TextLine line in _textLines)
{
totalHeight += line.Height;
}
drawPoint.Y = ((ActualHeight - (Padding.Top + Padding.Bottom)) / 2) - (totalHeight / 2);
break;
}
foreach (TextLine line in _textLines)
{
if (drawPoint.Y < 0)
{
drawPoint.Y += line.Height;
continue;
}
drawPoint.X = Padding.Left;
switch (HorizontalContentAlignment)
{
case HorizontalAlignment.Right:
drawPoint.X = ActualWidth - line.Width - Padding.Right;
break;
case HorizontalAlignment.Center:
drawPoint.X = ((ActualWidth - (Padding.Left + Padding.Right)) / 2) - (line.Width / 2);
break;
}
foreach (TextFragment fragment in line.FragmentList)
{
drawingContext.DrawText(fragment.FormattedText, drawPoint);
drawPoint.X += fragment.FormattedText.WidthIncludingTrailingWhitespace;
}
drawPoint.Y += line.Height;
}
}
public string Text
{
get
{
return (string) GetValue(TextProperty);
}
set
{
TextParser textParser = new TextParser();
_textLines = textParser.Parse(this, value);
SetValue(TextProperty, value);
}
}
}
}

View File

@@ -0,0 +1,63 @@
using System.Globalization;
using System.Windows;
using System.Windows.Media;
namespace Common.Wpf.HtmlLabelControl
{
public class TextFragment
{
private readonly HtmlLabel _parent;
public Brush Color { get; set; }
public FontStyle Style { get; set; }
public FontWeight Weight { get; set; }
public double Size { get; set; }
public string Text { get; set; }
public bool Underline { get; set; }
private Typeface _typeface;
public Typeface Typeface
{
get { return _typeface ?? (_typeface = new Typeface(_parent.FontFamily, Style, Weight, _parent.FontStretch)); }
}
private FormattedText _formattedText;
public FormattedText FormattedText
{
get
{
if (_formattedText == null)
{
string measureText = (string.IsNullOrEmpty(Text) ? " " : Text);
_formattedText = new FormattedText(measureText, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, Typeface, Size, Color);
TextDecorationCollection textDecorationCollection = new TextDecorationCollection();
if (Underline)
{
TextDecoration underlineDecoration = new TextDecoration { PenThicknessUnit = TextDecorationUnit.FontRecommended };
textDecorationCollection.Add(underlineDecoration);
}
_formattedText.SetTextDecorations(textDecorationCollection);
}
return _formattedText;
}
}
public TextFragment(HtmlLabel parent)
{
_parent = parent;
Color = _parent.Foreground;
Style = _parent.FontStyle;
Weight = _parent.FontWeight;
Size = _parent.FontSize;
Text = string.Empty;
Underline = false;
}
}
}

View File

@@ -0,0 +1,32 @@
using System.Windows;
using System.Windows.Media;
namespace Common.Wpf.HtmlLabelControl
{
public class TextFragmentStyle
{
public Brush Color { get; set; }
public FontStyle? Style { get; set; }
public FontWeight? Weight { get; set; }
public double? Size { get; set; }
public bool? Underline { get; set; }
public void Apply(TextFragment fragment)
{
if (Color != null)
fragment.Color = Color;
if (Style.HasValue)
fragment.Style = Style.Value;
if (Weight.HasValue)
fragment.Weight = Weight.Value;
if (Size.HasValue)
fragment.Size = Size.Value;
if (Underline.HasValue)
fragment.Underline = Underline.Value;
}
}
}

View File

@@ -0,0 +1,49 @@
using System.Collections.ObjectModel;
namespace Common.Wpf.HtmlLabelControl
{
public class TextLine
{
private double _height;
public double Height
{
get
{
if (_height == 0)
{
foreach (TextFragment textFragment in FragmentList)
{
if (textFragment.FormattedText.Height > _height)
_height = textFragment.FormattedText.Height;
}
}
return _height;
}
}
private double _width;
public double Width
{
get
{
if (_width == 0)
{
foreach (TextFragment textFragment in FragmentList)
{
_width += textFragment.FormattedText.Width;
}
}
return _width;
}
}
public Collection<TextFragment> FragmentList { get; private set; }
public TextLine()
{
FragmentList = new Collection<TextFragment>();
}
}
}

View File

@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows;
using System.Windows.Media;
using System.Xml;
namespace Common.Wpf.HtmlLabelControl
{
public class TextParser
{
private HtmlLabel _parentControl;
public Collection<TextLine> Parse(HtmlLabel parentControl, string text)
{
_parentControl = parentControl;
// Add a root tag so the parser is happy
text = string.Format(CultureInfo.InvariantCulture, "<body>{0}</body>", text);
// Normalize line endings
text = text.Replace("\r\n", "\n");
// Create an XML document and load it with the text
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(text);
// Create a list of text lines
Collection<TextLine> lines = new Collection<TextLine>();
// Walk over the nodes and build up the fragment list
walkNodes(xmlDocument.ChildNodes, lines);
return lines;
}
private readonly Stack<TextFragmentStyle> _attributeStack = new Stack<TextFragmentStyle>();
private void walkNodes(XmlNodeList xmlNodeList, Collection<TextLine> textLines)
{
if (textLines.Count == 0)
textLines.Add(new TextLine());
foreach (XmlNode xmlNode in xmlNodeList)
{
TextFragmentStyle style;
switch (xmlNode.Name.ToUpperInvariant())
{
case "#WHITESPACE":
case "#TEXT":
// Split the fragment and the line endings
string[] lines = xmlNode.Value.Split('\n');
bool firstLine = true;
foreach (string line in lines)
{
TextLine textLine = (firstLine ? textLines[textLines.Count - 1] : new TextLine());
// Create a new fragment and fill the style information
TextFragment textFragment = new TextFragment(_parentControl);
textFragment.Text = line;
foreach (TextFragmentStyle s in _attributeStack)
{
s.Apply(textFragment);
}
// Add the fragment to the list
textLine.FragmentList.Add(textFragment);
if (!firstLine)
textLines.Add(textLine);
firstLine = false;
}
break;
case "B":
style = new TextFragmentStyle { Weight = FontWeights.Bold };
_attributeStack.Push(style);
break;
case "U":
style = new TextFragmentStyle { Underline = true };
_attributeStack.Push(style);
break;
case "CITE":
style = new TextFragmentStyle { Style = FontStyles.Italic };
_attributeStack.Push(style);
break;
case "FONT":
style = new TextFragmentStyle();
foreach (XmlAttribute attribute in xmlNode.Attributes)
{
switch (attribute.Name.ToUpperInvariant())
{
case "SIZE":
style.Size = Convert.ToDouble(attribute.Value, CultureInfo.InvariantCulture);
break;
case "COLOR":
style.Color = (Brush) new BrushConverter().ConvertFromString(attribute.Value);
break;
}
}
_attributeStack.Push(style);
break;
}
if (xmlNode.ChildNodes.Count > 0)
walkNodes(xmlNode.ChildNodes, textLines);
if (_attributeStack.Count > 0)
_attributeStack.Pop();
}
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace Common.Wpf.HtmlTextBlock
{
public class HtmlTextBlock : TextBlock
{
public static readonly DependencyProperty HtmlProperty = DependencyProperty.Register("Html", typeof(string), typeof(HtmlTextBlock), new UIPropertyMetadata("Html", OnHtmlChanged));
public static void OnHtmlChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
HtmlTextBlock htmlTextBlock = (HtmlTextBlock) s;
Parse(htmlTextBlock, e.NewValue as string);
}
private static void Parse(HtmlTextBlock control, string value)
{
try
{
control.Inlines.Clear();
TextParser parser = new TextParser();
var lines = parser.Parse(control, value);
foreach (TextLine line in lines)
{
foreach (TextFragment fragment in line.FragmentList)
{
Run run = new Run(fragment.Text);
run.FontStyle = fragment.Style;
run.FontWeight = fragment.Weight;
run.FontSize = fragment.Size;
run.Foreground = fragment.Color;
control.Inlines.Add(run);
}
}
}
catch (Exception)
{
control.Inlines.Clear();
control.Text = value;
}
}
public string Html
{
get { return (string) GetValue(HtmlProperty); }
set { SetValue(HtmlProperty, value); }
}
}
}

View File

@@ -0,0 +1,63 @@
using System.Globalization;
using System.Windows;
using System.Windows.Media;
namespace Common.Wpf.HtmlTextBlock
{
public class TextFragment
{
private readonly HtmlTextBlock _parent;
public Brush Color { get; set; }
public FontStyle Style { get; set; }
public FontWeight Weight { get; set; }
public double Size { get; set; }
public string Text { get; set; }
public bool Underline { get; set; }
private Typeface _typeface;
public Typeface Typeface
{
get { return _typeface ?? (_typeface = new Typeface(_parent.FontFamily, Style, Weight, _parent.FontStretch)); }
}
private FormattedText _formattedText;
public FormattedText FormattedText
{
get
{
if (_formattedText == null)
{
string measureText = (string.IsNullOrEmpty(Text) ? " " : Text);
_formattedText = new FormattedText(measureText, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, Typeface, Size, Color);
TextDecorationCollection textDecorationCollection = new TextDecorationCollection();
if (Underline)
{
TextDecoration underlineDecoration = new TextDecoration { PenThicknessUnit = TextDecorationUnit.FontRecommended };
textDecorationCollection.Add(underlineDecoration);
}
_formattedText.SetTextDecorations(textDecorationCollection);
}
return _formattedText;
}
}
public TextFragment(HtmlTextBlock parent)
{
_parent = parent;
Color = _parent.Foreground;
Style = _parent.FontStyle;
Weight = _parent.FontWeight;
Size = _parent.FontSize;
Text = string.Empty;
Underline = false;
}
}
}

View File

@@ -0,0 +1,32 @@
using System.Windows;
using System.Windows.Media;
namespace Common.Wpf.HtmlTextBlock
{
public class TextFragmentStyle
{
public Brush Color { get; set; }
public FontStyle? Style { get; set; }
public FontWeight? Weight { get; set; }
public double? Size { get; set; }
public bool? Underline { get; set; }
public void Apply(TextFragment fragment)
{
if (Color != null)
fragment.Color = Color;
if (Style.HasValue)
fragment.Style = Style.Value;
if (Weight.HasValue)
fragment.Weight = Weight.Value;
if (Size.HasValue)
fragment.Size = Size.Value;
if (Underline.HasValue)
fragment.Underline = Underline.Value;
}
}
}

14
HtmlTextBlock/TextLine.cs Normal file
View File

@@ -0,0 +1,14 @@
using System.Collections.ObjectModel;
namespace Common.Wpf.HtmlTextBlock
{
public class TextLine
{
public Collection<TextFragment> FragmentList { get; private set; }
public TextLine()
{
FragmentList = new Collection<TextFragment>();
}
}
}

140
HtmlTextBlock/TextParser.cs Normal file
View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows;
using System.Windows.Media;
using System.Xml;
namespace Common.Wpf.HtmlTextBlock
{
public class TextParser
{
private HtmlTextBlock _parentControl;
public Collection<TextLine> Parse(HtmlTextBlock parentControl, string text)
{
_parentControl = parentControl;
text = text.Replace("&", "&amp;");
// Add a root tag so the parser is happy
text = string.Format(CultureInfo.InvariantCulture, "<body>{0}</body>", text);
// Normalize line endings
text = text.Replace("\r\n", "\n");
// Create an XML document and load it with the text
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(text);
// Create a list of text lines
Collection<TextLine> lines = new Collection<TextLine>();
// Walk over the nodes and build up the fragment list
WalkNodes(xmlDocument.ChildNodes, lines);
return lines;
}
private readonly Stack<TextFragmentStyle> _attributeStack = new Stack<TextFragmentStyle>();
private void WalkNodes(XmlNodeList xmlNodeList, Collection<TextLine> textLines)
{
if (textLines.Count == 0)
textLines.Add(new TextLine());
foreach (XmlNode xmlNode in xmlNodeList)
{
TextFragmentStyle style;
switch (xmlNode.Name.ToUpperInvariant())
{
case "#WHITESPACE":
case "#TEXT":
// Split the fragment and the line endings
string[] lines = xmlNode.Value.Split('\n');
bool firstLine = true;
foreach (string line in lines)
{
TextLine textLine = (firstLine ? textLines[textLines.Count - 1] : new TextLine());
// Create a new fragment and fill the style information
TextFragment textFragment = new TextFragment(_parentControl);
textFragment.Text = line;
foreach (TextFragmentStyle s in _attributeStack)
{
s.Apply(textFragment);
}
// Add the fragment to the list
textLine.FragmentList.Add(textFragment);
if (!firstLine)
textLines.Add(textLine);
firstLine = false;
}
break;
case "EM":
case "B":
style = new TextFragmentStyle { Weight = FontWeights.Bold };
_attributeStack.Push(style);
break;
case "U":
style = new TextFragmentStyle { Underline = true };
_attributeStack.Push(style);
break;
case "CITE":
style = new TextFragmentStyle { Style = FontStyles.Italic };
_attributeStack.Push(style);
break;
case "FONT":
style = new TextFragmentStyle();
if (xmlNode.Attributes == null)
break;
foreach (XmlAttribute attribute in xmlNode.Attributes)
{
switch (attribute.Name.ToUpperInvariant())
{
case "SIZE":
style.Size = Convert.ToDouble(attribute.Value, CultureInfo.InvariantCulture);
break;
case "COLOR":
style.Color = (Brush) new BrushConverter().ConvertFromString(attribute.Value);
break;
}
}
_attributeStack.Push(style);
break;
}
if (xmlNode.ChildNodes.Count > 0)
WalkNodes(xmlNode.ChildNodes, textLines);
if (_attributeStack.Count > 0)
_attributeStack.Pop();
}
}
}
}

View File

@@ -0,0 +1,12 @@
<UserControl x:Class="Common.Wpf.LinkControl.LinkControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<Hyperlink Name="HyperlinkControl">
<TextBlock Name="ContentControl" />
</Hyperlink>
</UserControl>

View File

@@ -0,0 +1,38 @@
using System.Windows;
namespace Common.Wpf.LinkControl
{
public partial class LinkControl
{
public event RoutedEventHandler Click;
public LinkControl()
{
InitializeComponent();
HyperlinkControl.Click += HandleHyperlinkControlClick;
}
private void HandleHyperlinkControlClick(object sender, RoutedEventArgs e)
{
if (Click != null)
Click.Invoke(sender, e);
}
public string Text
{
get { return ContentControl.Text; }
set { ContentControl.Text = value; }
}
public new bool IsEnabled
{
get { return base.IsEnabled; }
set
{
base.IsEnabled = value;
HyperlinkControl.IsEnabled = value;
}
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Common.Wpf")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("Common.Wpf")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: CLSCompliant(true)]
// 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)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,29 @@
<ListBox x:Class="Common.Wpf.TextListControl.TextList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
TextWrapping="Wrap"
Margin="0,1" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="MouseEnter"
Handler="handleListItemMouseEnter" />
<EventSetter Event="MouseLeave"
Handler="handleListItemMouseLeave" />
<EventSetter Event="MouseUp"
Handler="handleListItemMouseUp" />
<EventSetter Event="MouseDoubleClick"
Handler="handleListItemMouseDoubleClick" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

View File

@@ -0,0 +1,95 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Common.Wpf.TextListControl
{
public partial class TextList : ListBox
{
#region Events
public static RoutedEvent ListItemMouseUpEvent = EventManager.RegisterRoutedEvent("ListItemMouseUp", RoutingStrategy.Bubble, typeof(MouseButtonEventArgs), typeof(TextList));
public event MouseButtonEventHandler ListItemMouseUp;
public void OnListItemMouseUp(object listBoxItem, MouseButtonEventArgs e)
{
MouseButtonEventHandler handler = ListItemMouseUp;
if (handler != null) handler(listBoxItem, e);
}
public static RoutedEvent ListItemMouseDoubleClickEvent = EventManager.RegisterRoutedEvent("ListItemMouseDoubleClick", RoutingStrategy.Bubble, typeof(MouseButtonEventArgs), typeof(TextList));
public event MouseButtonEventHandler ListItemMouseDoubleClick;
public void OnListItemMouseDoubleClick(object listBoxItem, MouseButtonEventArgs e)
{
MouseButtonEventHandler handler = ListItemMouseDoubleClick;
if (handler != null) handler(listBoxItem, e);
}
#endregion
#region Constructor
public TextList()
{
InitializeComponent();
}
#endregion
#region Hover selection events
private void handleListItemMouseEnter(object sender, MouseEventArgs e)
{
// Make sure the control has focus
Focus();
// Get the list box item
ListBoxItem listBoxItem = (ListBoxItem) sender;
// Select the data context
SelectedItem = listBoxItem.DataContext;
// Set the cursor
listBoxItem.Cursor = Cursors.Hand;
}
private void handleListItemMouseLeave(object sender, MouseEventArgs e)
{
// Clear selection
SelectedItem = null;
}
#endregion
#region List item events
private void handleListItemMouseUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
// Get the list box item
ListBoxItem listBoxItem = (ListBoxItem) sender;
// Build the event args
MouseButtonEventArgs eventArgs = new MouseButtonEventArgs(mouseButtonEventArgs.MouseDevice, mouseButtonEventArgs.Timestamp, mouseButtonEventArgs.ChangedButton);
eventArgs.RoutedEvent = ListItemMouseUpEvent;
// Raise the event
OnListItemMouseUp(listBoxItem, eventArgs);
}
private void handleListItemMouseDoubleClick(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
// Get the list box item
ListBoxItem listBoxItem = (ListBoxItem) sender;
// Build the event args
MouseButtonEventArgs eventArgs = new MouseButtonEventArgs(mouseButtonEventArgs.MouseDevice, mouseButtonEventArgs.Timestamp, mouseButtonEventArgs.ChangedButton);
eventArgs.RoutedEvent = ListItemMouseDoubleClickEvent;
// Raise the event
OnListItemMouseDoubleClick(listBoxItem, eventArgs);
}
#endregion
}
}

View File

@@ -0,0 +1,97 @@
// --------------------------------
// Copyright (c) Huy Pham. All rights reserved.
// This source code is made available under the terms of the Microsoft Public License (Ms-PL)
// http://www.opensource.org/licenses/ms-pl.html
// ---------------------------------
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
namespace Common.Wpf.Toolbar.DropDownButton
{
public class DropDownButton : ToggleButton
{
#region Dependency Properties
public static readonly DependencyProperty DropDownContextMenuProperty = DependencyProperty.Register("DropDownContextMenu", typeof(ContextMenu), typeof(DropDownButton), new UIPropertyMetadata(null));
public static readonly DependencyProperty ImageProperty = DependencyProperty.Register("Image", typeof(ImageSource), typeof(DropDownButton));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(DropDownButton));
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register("Target", typeof(UIElement), typeof(DropDownButton));
public static readonly DependencyProperty DropDownButtonCommandProperty = DependencyProperty.Register("DropDownButtonCommand", typeof(ICommand), typeof(DropDownButton), new FrameworkPropertyMetadata(null));
#endregion
#region Constructors
public DropDownButton()
{
// Bind the ToogleButton.IsChecked property to the drop-down's IsOpen property
var binding = new Binding("DropDownContextMenu.IsOpen") {Source = this};
SetBinding(IsCheckedProperty, binding);
}
#endregion
#region Properties
public ContextMenu DropDownContextMenu
{
get { return GetValue(DropDownContextMenuProperty) as ContextMenu; }
set { SetValue(DropDownContextMenuProperty, value); }
}
public ImageSource Image
{
get { return GetValue(ImageProperty) as ImageSource; }
set { SetValue(ImageProperty, value); }
}
public string Text
{
get { return GetValue(TextProperty) as string; }
set { SetValue(TextProperty, value); }
}
public UIElement Target
{
get { return GetValue(TargetProperty) as UIElement; }
set { SetValue(TargetProperty, value); }
}
public ICommand DropDownButtonCommand
{
get { return GetValue(DropDownButtonCommandProperty) as ICommand; }
set { SetValue(DropDownButtonCommandProperty, value); }
}
#endregion
#region Protected Override Methods
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == DropDownButtonCommandProperty)
Command = DropDownButtonCommand;
}
protected override void OnClick()
{
if (DropDownContextMenu == null) return;
if (DropDownButtonCommand != null) DropDownButtonCommand.Execute(null);
// If there is a drop-down assigned to this button, then position and display it
DropDownContextMenu.PlacementTarget = this;
DropDownContextMenu.Placement = PlacementMode.Bottom;
DropDownContextMenu.IsOpen = !DropDownContextMenu.IsOpen;
}
#endregion
}
}

View File

@@ -0,0 +1,226 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Luna"
xmlns:DropDownButton="clr-namespace:Common.Wpf.Toolbar.DropDownButton">
<!-- Used for Checkmark, Radio button, TreeViewItem, Expander ToggleButton glyphs -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444"/>
<!-- SelectedBackgroundBrush is used for the Selected item in ListBoxItem, ComboBoxItem-->
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD"/>
<!-- Disabled Brushes are used for the Disabled look of each control -->
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888"/>
<!-- NormalBrush is used as the Background for SimpleButton, SimpleRepeatButton -->
<LinearGradientBrush x:Key="NormalBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#EEE" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="NormalBorderBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</LinearGradientBrush>
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle SnapsToDevicePixels="true" Margin="3" Stroke="Black" StrokeDashArray="1 2" StrokeThickness="1"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<LinearGradientBrush x:Key="ButtonNormalBackgroundFill" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFFFFFFF" Offset="0"/>
<GradientStop Color="#FFF0F0EA" Offset="0.9"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ButtonBorder" Color="#FF003C74"/>
<Style x:Key="LabelStyle" TargetType="{x:Type Label}" BasedOn="{x:Null}">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Grid>
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RecognizesAccessKey="True"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type DropDownButton:DropDownButton}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackgroundFill}"/>
<Setter Property="BorderBrush" Value="{StaticResource ButtonBorder}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="MinHeight" Value="22" />
<Setter Property="MinWidth" Value="80" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DropDownButton:DropDownButton}">
<themes:ButtonChrome x:Name="Chrome" SnapsToDevicePixels="True" BorderBrush="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" ThemeColor="NormalColor">
<Label Style="{StaticResource LabelStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image x:Name="icon" Grid.Column="0" Margin="4,0,2,0" VerticalAlignment="Center" Width="16" Height="16" Source="{TemplateBinding Image}" />
<Label Grid.Column="1" Content="{TemplateBinding Text}" Target="{TemplateBinding Target}" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="0" Margin="0,0,3,0" Foreground="{TemplateBinding Foreground}"/>
<Path Grid.Column="2" Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z" Height="5" Margin="3,2,2,0"/>
</Grid>
</Label>
</themes:ButtonChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="RenderDefaulted" TargetName="Chrome" Value="True"/>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter Property="RenderPressed" TargetName="Chrome" Value="True"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ToolBarDropDownButtonStyle" TargetType="{x:Type DropDownButton:DropDownButton}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1,1,1,1"/>
<Setter Property="Background" Value="#00FFFFFF"/>
<Setter Property="BorderBrush" Value="#00FFFFFF"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DropDownButton:DropDownButton}">
<Border SnapsToDevicePixels="True" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<!--<Label>-->
<StackPanel Orientation="Horizontal">
<Image Margin="2,2,2,2" VerticalAlignment="Center" Width="16" Height="16" Source="{TemplateBinding Image}"/>
<Label Content="{TemplateBinding Text}" Target="{TemplateBinding Target}" VerticalAlignment="Center" Padding="0" />
<Path Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z" Height="5" Margin="5,2,2,0"/>
</StackPanel>
<!--</Label>-->
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="BorderBrush" Value="#FF316AC5"/>
<Setter Property="Background" Value="#FFE1E6E8"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="#FF316AC5"/>
<Setter Property="Background" Value="#FFC1D2EE"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="BorderBrush" Value="#FF316AC5"/>
<Setter Property="Background" Value="#FFC1D2EE"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="#FF4B4B6F"/>
<Setter Property="Background" Value="#FF98B5E2"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocused" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="#FF4B4B6F"/>
<Setter Property="Background" Value="#FF98B5E2"/>
</MultiTrigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="BorderBrush" Value="#FF4B4B6F"/>
<Setter Property="Background" Value="#FF98B5E2"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="DropDownMenuIconStyle">
<Image Source="{Binding XPath=.}"></Image>
</DataTemplate>
<ControlTemplate x:Key="DropDownMenuItemTemplate" TargetType="MenuItem">
<Border Name="Border">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Icon"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Shortcut"/>
<ColumnDefinition Width="13"/>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon"
ContentTemplate="{StaticResource DropDownMenuIconStyle}"
Margin="6,0,6,0"
VerticalAlignment="Center"
ContentSource="Icon"
Height="16" Width="16"/>
<Border x:Name="Check"
Width="13" Height="13"
Visibility="Collapsed"
Margin="6,0,6,0"
Background="{StaticResource NormalBrush}"
BorderThickness="1"
BorderBrush="{StaticResource NormalBorderBrush}">
<Path x:Name="CheckMark"
Width="7" Height="7"
Visibility="Hidden"
SnapsToDevicePixels="False"
Stroke="{StaticResource GlyphBrush}"
StrokeThickness="2"
Data="M 0 0 L 7 7 M 0 7 L 7 0" />
</Border>
<ContentPresenter x:Name="HeaderHost"
Grid.Column="1"
ContentSource="Header"
RecognizesAccessKey="True"
Margin="5,2,0,2"
VerticalAlignment="Center"/>
<TextBlock x:Name="InputGestureText"
Grid.Column="2"
Text="{TemplateBinding InputGestureText}"
Margin="5,2,0,2"
DockPanel.Dock="Right" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Icon" Value="{x:Null}">
<Setter TargetName="Icon" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsCheckable" Value="true">
<Setter TargetName="Check" Property="Visibility" Value="Visible"/>
<Setter TargetName="Icon" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ResourceDictionary>

25
Toolbar/ImageButton.xaml Normal file
View File

@@ -0,0 +1,25 @@
<Button x:Class="Common.Wpf.Toolbar.ImageButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="20"
d:DesignWidth="20">
<Button.Resources>
<Style TargetType="{x:Type Image}"
x:Key="toolbarImageStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}, AncestorLevel=1}, Path=IsEnabled}"
Value="False">
<Setter Property="Opacity"
Value="0.25" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Resources>
<Image x:Name="image"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Style="{StaticResource toolbarImageStyle}" />
</Button>

View File

@@ -0,0 +1,30 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Common.Wpf.Toolbar
{
public partial class ImageButton
{
public ImageButton()
{
InitializeComponent();
Loaded += HandleImageButtonLoaded;
}
void HandleImageButtonLoaded(object sender, RoutedEventArgs e)
{
if (Style == null && Parent is ToolBar)
{
Style = (Style) FindResource(ToolBar.ButtonStyleKey);
}
}
public ImageSource ImageSource
{
get { return image.Source; }
set { image.Source = value; }
}
}
}

View File

@@ -0,0 +1,150 @@
// --------------------------------
// Copyright (c) Huy Pham. All rights reserved.
// This source code is made available under the terms of the Microsoft Public License (Ms-PL)
// http://www.opensource.org/licenses/ms-pl.html
// ---------------------------------
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
namespace Common.Wpf.Toolbar.SplitButton
{
[TemplatePart(Name = "PART_Button", Type = typeof(ButtonBase))]
public class SplitButton : ToggleButton
{
#region Dependency Properties
public static readonly DependencyProperty DropDownContextMenuProperty = DependencyProperty.Register("DropDownContextMenu", typeof(ContextMenu), typeof(SplitButton), new UIPropertyMetadata(null));
public static readonly DependencyProperty ImageProperty = DependencyProperty.Register("Image", typeof(ImageSource), typeof(SplitButton));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(SplitButton));
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register("Target", typeof(UIElement), typeof(SplitButton));
public static readonly DependencyProperty MainButtonCommandProperty = DependencyProperty.Register("MainButtonCommand", typeof(ICommand), typeof(SplitButton), new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty DropDownButtonCommandProperty = DependencyProperty.Register("DropDownButtonCommand", typeof(ICommand), typeof(SplitButton), new FrameworkPropertyMetadata(null));
#endregion
#region Constructors
public SplitButton()
{
// Bind the ToogleButton.IsChecked property to the drop-down's IsOpen property
var binding = new Binding("DropDownContextMenu.IsOpen") { Source = this };
SetBinding(IsCheckedProperty, binding);
Loaded += HandleSplitButtonLoaded;
}
void HandleSplitButtonLoaded(object sender, RoutedEventArgs e)
{
if (Parent is ToolBar)
{
Style style = (Style) TryFindResource("ToolBarSplitButtonStyle");
if (style != null)
Style = style;
}
Loaded -= HandleSplitButtonLoaded;
}
#endregion
#region Properties
public ContextMenu DropDownContextMenu
{
get { return GetValue(DropDownContextMenuProperty) as ContextMenu; }
set { SetValue(DropDownContextMenuProperty, value); }
}
public ImageSource Image
{
get { return GetValue(ImageProperty) as ImageSource; }
set { SetValue(ImageProperty, value); }
}
public string Text
{
get { return GetValue(TextProperty) as string; }
set { SetValue(TextProperty, value); }
}
public UIElement Target
{
get { return GetValue(TargetProperty) as UIElement; }
set { SetValue(TargetProperty, value); }
}
public ICommand MainButtonCommand
{
get { return GetValue(MainButtonCommandProperty) as ICommand; }
set { SetValue(MainButtonCommandProperty, value); }
}
public ICommand DropDownButtonCommand
{
get { return GetValue(DropDownButtonCommandProperty) as ICommand; }
set { SetValue(DropDownButtonCommandProperty, value); }
}
#endregion
#region Public Override Methods
/// <summary>
///
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
SetMainButtonCommand();
}
#endregion
#region Protected Override Methods
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == MainButtonCommandProperty)
SetMainButtonCommand();
if (e.Property == DropDownButtonCommandProperty)
Command = DropDownButtonCommand;
}
protected override void OnClick()
{
if (DropDownContextMenu == null) return;
if (DropDownButtonCommand != null)
DropDownButtonCommand.Execute(null);
// If there is a drop-down assigned to this button, then position and display it
DropDownContextMenu.PlacementTarget = this;
DropDownContextMenu.Placement = PlacementMode.Bottom;
DropDownContextMenu.IsOpen = !DropDownContextMenu.IsOpen;
}
#endregion
#region Private Methods
private void SetMainButtonCommand()
{
// Set up the event handlers
if (Template != null)
{
var button = Template.FindName("PART_Button", this) as ButtonBase;
if (button != null) button.Command = MainButtonCommand;
}
}
#endregion
}
}

View File

@@ -0,0 +1,277 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:Common.Wpf.Toolbar.SplitButton"
xmlns:themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Luna">
<!-- Used for Checkmark, Radio button, TreeViewItem, Expander ToggleButton glyphs -->
<SolidColorBrush x:Key="GlyphBrush" Color="#444"/>
<!-- SelectedBackgroundBrush is used for the Selected item in ListBoxItem, ComboBoxItem-->
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD"/>
<!-- Disabled Brushes are used for the Disabled look of each control -->
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888"/>
<!-- NormalBrush is used as the Background for SimpleButton, SimpleRepeatButton -->
<LinearGradientBrush x:Key="NormalBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#EEE" Offset="0.0"/>
<GradientStop Color="#CCC" Offset="1.0"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="NormalBorderBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</LinearGradientBrush>
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle SnapsToDevicePixels="true" Margin="3" Stroke="Black" StrokeDashArray="1 2" StrokeThickness="1"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<LinearGradientBrush x:Key="ButtonNormalBackgroundFill" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFFFFFFF" Offset="0"/>
<GradientStop Color="#FFF0F0EA" Offset="0.9"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ButtonBorder" Color="#FF003C74"/>
<Style x:Key="MainButtonStyle" TargetType="{x:Type Button}" BasedOn="{x:Null}">
<Setter Property="Margin" Value="-2,-2,0,-2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle x:Name="rect" Fill="Transparent" Stroke="#00000000" />
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True"/>
<Trigger Property="IsDefaulted" Value="True"/>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" TargetName="rect" Value="#00FFFFFF"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Fill" TargetName="rect" Value="#E3E3DC"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False"/>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ctrl:SplitButton}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackgroundFill}"/>
<Setter Property="BorderBrush" Value="{StaticResource ButtonBorder}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="MinHeight" Value="22" />
<Setter Property="MinWidth" Value="80" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ctrl:SplitButton}">
<themes:ButtonChrome x:Name="Chrome" SnapsToDevicePixels="True" BorderBrush="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" ThemeColor="NormalColor">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button x:Name="PART_Button" Grid.Column="0" Style="{DynamicResource MainButtonStyle}">
<StackPanel Orientation="Horizontal">
<Image x:Name="icon" Margin="4,0,2,0" VerticalAlignment="Center" Width="16" Height="16" Source="{TemplateBinding Image}" />
<Label Content="{TemplateBinding Text}" Target="{TemplateBinding Target}" VerticalAlignment="Center" Padding="0" Margin="0,0,3,0" Foreground="{TemplateBinding Foreground}"/>
</StackPanel>
</Button>
<Border x:Name="line" Grid.Column="1" Visibility="Visible" BorderThickness="1,0,0,0" BorderBrush="#3F6C96" Margin="0,-2,0,-2"/>
<Border x:Name="lineGrey" Grid.Column="2" Visibility="Visible" BorderThickness="1,0,0,0" BorderBrush="#B9CADA" Margin="0,-2,0,-2"/>
<Path Grid.Column="3" Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z" Height="5" Margin="3,2,2,0"/>
</Grid>
</themes:ButtonChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="RenderDefaulted" TargetName="Chrome" Value="True"/>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter Property="RenderPressed" TargetName="Chrome" Value="True"/>
<Setter Property="Visibility" Value="Hidden" TargetName="line"/>
<Setter Property="Visibility" Value="Hidden" TargetName="lineGrey"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
<Setter Property="Visibility" Value="Hidden" TargetName="line"/>
<Setter Property="Visibility" Value="Hidden" TargetName="lineGrey"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="#00FFFFFF"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Bd" SnapsToDevicePixels="True" Background="{TemplateBinding Background}">
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="Bd" Value="#FF98B5E2"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ToolBarSplitButtonStyle" TargetType="{x:Type ctrl:SplitButton}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1,1,1,1"/>
<Setter Property="Background" Value="#00FFFFFF"/>
<Setter Property="BorderBrush" Value="#00FFFFFF"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ctrl:SplitButton}">
<Border SnapsToDevicePixels="True" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel Orientation="Horizontal">
<Button x:Name="PART_Button" Style="{DynamicResource ButtonStyle}">
<StackPanel Orientation="Horizontal">
<Image Margin="2,2,2,2" VerticalAlignment="Center" Width="16" Height="16" Source="{TemplateBinding Image}"/>
<Label Content="{TemplateBinding Text}" Target="{TemplateBinding Target}" VerticalAlignment="Center" Padding="0" Margin="0,0,3,0"/>
</StackPanel>
</Button>
<Border x:Name="line" Visibility="Hidden" BorderThickness="1,0,0,0" BorderBrush="{TemplateBinding BorderBrush}" />
<Path Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z" Height="5" Margin="2,2,2,0"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="BorderBrush" Value="#FF316AC5"/>
<Setter Property="Background" Value="#FFE1E6E8"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="#FF316AC5"/>
<Setter Property="Background" Value="#FFC1D2EE"/>
<Setter Property="Visibility" Value="Visible" TargetName="line"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="BorderBrush" Value="#FF316AC5"/>
<Setter Property="Background" Value="#FFC1D2EE"/>
<Setter Property="Visibility" Value="Visible" TargetName="line"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="#FF4B4B6F"/>
<Setter Property="Background" Value="#FF98B5E2"/>
<Setter Property="Visibility" Value="Visible" TargetName="line"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocused" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="#FF4B4B6F"/>
<Setter Property="Background" Value="#FF98B5E2"/>
<Setter Property="Visibility" Value="Visible" TargetName="line"/>
</MultiTrigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="BorderBrush" Value="#FF4B4B6F"/>
<Setter Property="Background" Value="#FF98B5E2"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="SplitMenuIconStyle">
<Image Source="{Binding XPath=.}"></Image>
</DataTemplate>
<ControlTemplate x:Key="SplitMenuItemTemplate" TargetType="MenuItem">
<Border Name="Border">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Icon"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Shortcut"/>
<ColumnDefinition Width="13"/>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon"
ContentTemplate="{StaticResource SplitMenuIconStyle}"
Margin="6,0,6,0"
VerticalAlignment="Center"
ContentSource="Icon"
Height="16" Width="16"/>
<Border x:Name="Check"
Width="13" Height="13"
Visibility="Collapsed"
Margin="6,0,6,0"
Background="{StaticResource NormalBrush}"
BorderThickness="1"
BorderBrush="{StaticResource NormalBorderBrush}">
<Path x:Name="CheckMark"
Width="7" Height="7"
Visibility="Hidden"
SnapsToDevicePixels="False"
Stroke="{StaticResource GlyphBrush}"
StrokeThickness="2"
Data="M 0 0 L 7 7 M 0 7 L 7 0" />
</Border>
<ContentPresenter x:Name="HeaderHost"
Grid.Column="1"
ContentSource="Header"
RecognizesAccessKey="True"
Margin="5,2,0,2"
VerticalAlignment="Center"/>
<TextBlock x:Name="InputGestureText"
Grid.Column="2"
Text="{TemplateBinding InputGestureText}"
Margin="5,2,0,2"
DockPanel.Dock="Right" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Icon" Value="{x:Null}">
<Setter TargetName="Icon" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsCheckable" Value="true">
<Setter TargetName="Check" Property="Visibility" Value="Visible"/>
<Setter TargetName="Icon" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ResourceDictionary>

178
Windows/ControlBox.cs Normal file
View File

@@ -0,0 +1,178 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace Common.Wpf.Windows
{
public static class ControlBox
{
public static readonly DependencyProperty HasHelpButtonProperty = DependencyProperty.RegisterAttached(
"HasHelpButton",
typeof(bool),
typeof(ControlBox),
new UIPropertyMetadata(false, OnControlBoxChanged));
public static readonly DependencyProperty HasMaximizeButtonProperty = DependencyProperty.RegisterAttached(
"HasMaximizeButton",
typeof(bool),
typeof(ControlBox),
new UIPropertyMetadata(true, OnControlBoxChanged));
public static readonly DependencyProperty HasMinimizeButtonProperty = DependencyProperty.RegisterAttached(
"HasMinimizeButton",
typeof(bool),
typeof(ControlBox),
new UIPropertyMetadata(true, OnControlBoxChanged));
public static readonly DependencyProperty HasSystemMenuProperty = DependencyProperty.RegisterAttached(
"HasSystemMenu",
typeof(bool),
typeof(ControlBox),
new UIPropertyMetadata(true, OnControlBoxChanged));
private const int Style = -16;
private const int ExtStyle = -20;
private const int MaximizeBox = 0x10000;
private const int MinimizeBox = 0x20000;
private const int ContextHelp = 0x400;
private const int SystemMenu = 0x00080000;
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetHasHelpButton(Window element)
{
return (bool) element.GetValue(HasHelpButtonProperty);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetHasHelpButton(Window element, bool value)
{
element.SetValue(HasHelpButtonProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetHasMaximizeButton(Window element)
{
return (bool) element.GetValue(HasMaximizeButtonProperty);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetHasMaximizeButton(Window element, bool value)
{
element.SetValue(HasMaximizeButtonProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetHasMinimizeButton(Window element)
{
return (bool) element.GetValue(HasMinimizeButtonProperty);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetHasMinimizeButton(Window element, bool value)
{
element.SetValue(HasMinimizeButtonProperty, value);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetHasSystemMenu(Window element)
{
return (bool) element.GetValue(HasSystemMenuProperty);
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetHasSystemMenu(Window element, bool value)
{
element.SetValue(HasSystemMenuProperty, value);
}
private static void OnControlBoxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null)
{
var hWnd = new WindowInteropHelper(window).Handle;
if (hWnd == IntPtr.Zero)
{
window.SourceInitialized += OnWindowSourceInitialized;
}
else
{
UpdateStyle(window, hWnd);
UpdateExtendedStyle(window, hWnd);
}
}
}
private static void OnWindowSourceInitialized(object sender, EventArgs e)
{
var window = (Window) sender;
var hWnd = new WindowInteropHelper(window).Handle;
UpdateStyle(window, hWnd);
UpdateExtendedStyle(window, hWnd);
window.SourceInitialized -= OnWindowSourceInitialized;
}
private static void UpdateStyle(Window window, IntPtr hWnd)
{
var style = NativeMethods.GetWindowLong(hWnd, Style);
if (GetHasMaximizeButton(window))
{
style |= MaximizeBox;
}
else
{
style &= ~MaximizeBox;
}
if (GetHasMinimizeButton(window))
{
style |= MinimizeBox;
}
else
{
style &= ~MinimizeBox;
}
if (GetHasSystemMenu(window))
{
style |= SystemMenu;
}
else
{
style &= ~SystemMenu;
}
NativeMethods.SetWindowLong(hWnd, Style, style);
}
private static void UpdateExtendedStyle(Window window, IntPtr hWnd)
{
var style = NativeMethods.GetWindowLong(hWnd, ExtStyle);
if (GetHasHelpButton(window))
{
style |= ContextHelp;
}
else
{
style &= -~ContextHelp;
}
NativeMethods.SetWindowLong(hWnd, ExtStyle, style);
}
private static class NativeMethods
{
[DllImport("user32.dll")]
internal static extern int GetWindowLong(IntPtr hWnd, int index);
[DllImport("user32.dll")]
internal static extern int SetWindowLong(IntPtr hWnd, int index, int newLong);
}
}
}

324
Windows/SnappingWindow.cs Normal file
View File

@@ -0,0 +1,324 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using Common.Native;
using Rectangle = System.Drawing.Rectangle;
using Screen = System.Windows.Forms.Screen;
namespace Common.Wpf.Windows
{
public class SnappingWindow : Window
{
#region Member variables
private HwndSource _hwndSource;
private Structures.WindowPosition _lastWindowPosition;
#endregion
#region Enumerations
private enum SnapMode
{
Move,
Resize
}
#endregion
#region Properties
protected virtual int SnapDistance
{
get { return 20; }
}
protected virtual List<Rect> OtherWindows
{
get { return null; }
}
#endregion
#region Window overrides
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// Store the window handle
_hwndSource = PresentationSource.FromVisual(this) as HwndSource;
// If we failed to get the hwnd then don't bother
if (_hwndSource == null)
return;
// Add a hook
_hwndSource.AddHook(WndProc);
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// Initialize the last window position
_lastWindowPosition = new Structures.WindowPosition
{
Left = (int) Left,
Width = (int) Width,
Height = (int) Height,
Top = (int) Top
};
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// Unhook the window procedure
_hwndSource.RemoveHook(WndProc);
_hwndSource.Dispose();
}
#endregion
#region Window procedure
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == (int) Constants.WindowMessage.WindowPositionChanging)
return OnWindowPositionChanging(lParam, ref handled);
return IntPtr.Zero;
}
#endregion
#region Snapping
private IntPtr OnWindowPositionChanging(IntPtr lParam, ref bool handled)
{
int snapDistance = SnapDistance;
// Initialize whether we've updated the position
bool updated = false;
// Convert the lParam into the current structure
var windowPosition = (Structures.WindowPosition) Marshal.PtrToStructure(lParam, typeof(Structures.WindowPosition));
// If the window flags indicate no movement then do nothing
if ((windowPosition.Flags & Constants.WindowPositionFlags.NoMove) != 0)
return IntPtr.Zero;
// If nothing changed then do nothing
if (_lastWindowPosition.IsSameLocationAndSize(windowPosition))
return IntPtr.Zero;
// Figure out if the window is being moved or resized
SnapMode snapMode = (_lastWindowPosition.IsSameSize(windowPosition) ? SnapMode.Move : SnapMode.Resize);
// Get the screen the cursor is currently on
Screen screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
// Create a rectangle based on the current working area of the screen
Rectangle snapToBorder = screen.WorkingArea;
// Deflate the rectangle based on the snap distance
snapToBorder.Inflate(-snapDistance, -snapDistance);
if (snapMode == SnapMode.Resize)
{
// See if we need to snap on the left
if (windowPosition.Left < snapToBorder.Left)
{
windowPosition.Width += windowPosition.Left - screen.WorkingArea.Left;
windowPosition.Left = screen.WorkingArea.Left;
updated = true;
}
// See if we need to snap on the right
if (windowPosition.Right > snapToBorder.Right)
{
windowPosition.Width += (screen.WorkingArea.Right - windowPosition.Right);
updated = true;
}
// See if we need to snap to the top
if (windowPosition.Top < snapToBorder.Top)
{
windowPosition.Height += windowPosition.Top - screen.WorkingArea.Top;
windowPosition.Top = screen.WorkingArea.Top;
updated = true;
}
// See if we need to snap to the bottom
if (windowPosition.Bottom > snapToBorder.Bottom)
{
windowPosition.Height += (screen.WorkingArea.Bottom - windowPosition.Bottom);
updated = true;
}
}
else
{
// See if we need to snap on the left
if (windowPosition.Left < snapToBorder.Left)
{
windowPosition.Left = screen.WorkingArea.Left;
updated = true;
}
// See if we need to snap on the top
if (windowPosition.Top < snapToBorder.Top)
{
windowPosition.Top = screen.WorkingArea.Top;
updated = true;
}
// See if we need to snap on the right
if (windowPosition.Right > snapToBorder.Right)
{
windowPosition.Left = (screen.WorkingArea.Right - windowPosition.Width);
updated = true;
}
// See if we need to snap on the bottom
if (windowPosition.Bottom > snapToBorder.Bottom)
{
windowPosition.Top = (screen.WorkingArea.Bottom - windowPosition.Height);
updated = true;
}
}
var otherWindows = OtherWindows;
if (otherWindows != null && otherWindows.Count > 0)
{
// Loop over all other windows looking to see if we should stick
foreach (Rect otherWindow in otherWindows)
{
// Get a rectangle with the bounds of the other window
var otherWindowRect = new Rectangle(Convert.ToInt32(otherWindow.Left), Convert.ToInt32(otherWindow.Top), Convert.ToInt32(otherWindow.Width), Convert.ToInt32(otherWindow.Height));
// Check the current window left against the other window right
var otherWindowSnapBorder = new Rectangle(otherWindowRect.Right, otherWindowRect.Top, snapDistance, otherWindowRect.Height);
var thisWindowSnapBorder = new Rectangle(windowPosition.Left, windowPosition.Top, 1, windowPosition.Height);
if (thisWindowSnapBorder.IntersectsWith(otherWindowSnapBorder))
{
windowPosition.Left = otherWindowRect.Right;
CheckSnapTopAndBottom(ref windowPosition, otherWindowRect, snapMode);
updated = true;
}
// Check the current window right against the other window left
otherWindowSnapBorder = new Rectangle(otherWindowRect.Left - snapDistance + 1, otherWindowRect.Top, snapDistance, otherWindowRect.Height);
thisWindowSnapBorder = new Rectangle(windowPosition.Right, windowPosition.Top, 1, windowPosition.Height);
if (thisWindowSnapBorder.IntersectsWith(otherWindowSnapBorder))
{
windowPosition.Left = otherWindowRect.Left - windowPosition.Width;
CheckSnapTopAndBottom(ref windowPosition, otherWindowRect, snapMode);
updated = true;
}
// Check the current window bottom against the other window top
otherWindowSnapBorder = new Rectangle(otherWindowRect.Left, otherWindowRect.Top - snapDistance + 1, otherWindowRect.Width, snapDistance);
thisWindowSnapBorder = new Rectangle(windowPosition.Left, windowPosition.Bottom, windowPosition.Width, 1);
if (thisWindowSnapBorder.IntersectsWith(otherWindowSnapBorder))
{
windowPosition.Top = otherWindowRect.Top - windowPosition.Height;
CheckSnapLeftAndRight(ref windowPosition, otherWindowRect, snapMode);
updated = true;
}
// Check the current window top against the other window bottom
otherWindowSnapBorder = new Rectangle(otherWindowRect.Left, otherWindowRect.Bottom, otherWindowRect.Width, snapDistance);
thisWindowSnapBorder = new Rectangle(windowPosition.Left, windowPosition.Top, windowPosition.Width, 1);
if (thisWindowSnapBorder.IntersectsWith(otherWindowSnapBorder))
{
windowPosition.Top = otherWindowRect.Bottom;
CheckSnapLeftAndRight(ref windowPosition, otherWindowRect, snapMode);
updated = true;
}
}
}
// Update the last window position
_lastWindowPosition = windowPosition;
if (updated)
{
Marshal.StructureToPtr(windowPosition, lParam, true);
handled = true;
}
return IntPtr.Zero;
}
private void CheckSnapTopAndBottom(ref Structures.WindowPosition windowPosition, Rectangle otherWindowRect, SnapMode snapMode)
{
int snapDistance = SnapDistance;
switch (snapMode)
{
case SnapMode.Move:
if (Math.Abs(windowPosition.Top - otherWindowRect.Top) <= snapDistance)
windowPosition.Top = otherWindowRect.Top;
else if (Math.Abs(windowPosition.Bottom - otherWindowRect.Bottom) <= snapDistance)
windowPosition.Top = otherWindowRect.Bottom - windowPosition.Height;
break;
case SnapMode.Resize:
if (Math.Abs(windowPosition.Top - otherWindowRect.Top) <= snapDistance)
{
windowPosition.Height += (windowPosition.Top - otherWindowRect.Top);
windowPosition.Top = otherWindowRect.Top;
}
else
if (Math.Abs(windowPosition.Bottom - otherWindowRect.Bottom) <= snapDistance)
windowPosition.Height = otherWindowRect.Bottom - windowPosition.Top;
break;
}
}
private void CheckSnapLeftAndRight(ref Structures.WindowPosition windowPosition, Rectangle otherWindowRect, SnapMode snapMode)
{
int snapDistance = SnapDistance;
switch (snapMode)
{
case SnapMode.Move:
if (Math.Abs(windowPosition.Left - otherWindowRect.Left) <= snapDistance)
windowPosition.Left = otherWindowRect.Left;
else if (Math.Abs(windowPosition.Right - otherWindowRect.Right) <= snapDistance)
windowPosition.Left = otherWindowRect.Right - windowPosition.Width;
break;
case SnapMode.Resize:
if (Math.Abs(windowPosition.Left - otherWindowRect.Left) <= snapDistance)
{
windowPosition.Width += (windowPosition.Left - otherWindowRect.Left);
windowPosition.Left = otherWindowRect.Left;
}
else
if (Math.Abs(windowPosition.Right - otherWindowRect.Right) <= snapDistance)
windowPosition.Width = otherWindowRect.Right - windowPosition.Left;
break;
}
}
#endregion
}
}