mirror of
https://github.com/ckaczor/Common.Wpf.git
synced 2026-01-13 17:22:47 -05:00
Initial commit
This commit is contained in:
50
CheckedListItem.cs
Normal file
50
CheckedListItem.cs
Normal 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
245
Common.Wpf.csproj
Normal 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>
|
||||
32
ExtendedListBoxControl/ExtendedListBox.cs
Normal file
32
ExtendedListBoxControl/ExtendedListBox.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
ExtendedListBoxControl/ExtendedListBoxItem.cs
Normal file
75
ExtendedListBoxControl/ExtendedListBoxItem.cs
Normal 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
|
||||
}
|
||||
}
|
||||
111
Extensions/ApplicationExtensions.cs
Normal file
111
Extensions/ApplicationExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Extensions/DependencyObjectExtensions.cs
Normal file
20
Extensions/DependencyObjectExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Extensions/FontExtensions.cs
Normal file
43
Extensions/FontExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Extensions/GridExtensions.cs
Normal file
18
Extensions/GridExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
103
Extensions/WindowExtensions.cs
Normal file
103
Extensions/WindowExtensions.cs
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
7
HtmlLabelControl/HtmlLabel.xaml
Normal file
7
HtmlLabelControl/HtmlLabel.xaml
Normal 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>
|
||||
122
HtmlLabelControl/HtmlLabel.xaml.cs
Normal file
122
HtmlLabelControl/HtmlLabel.xaml.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
HtmlLabelControl/TextFragment.cs
Normal file
63
HtmlLabelControl/TextFragment.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
HtmlLabelControl/TextFragmentStyle.cs
Normal file
32
HtmlLabelControl/TextFragmentStyle.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
HtmlLabelControl/TextLine.cs
Normal file
49
HtmlLabelControl/TextLine.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
134
HtmlLabelControl/TextParser.cs
Normal file
134
HtmlLabelControl/TextParser.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
HtmlTextBlock/HtmlTextBlock.cs
Normal file
57
HtmlTextBlock/HtmlTextBlock.cs
Normal 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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
63
HtmlTextBlock/TextFragment.cs
Normal file
63
HtmlTextBlock/TextFragment.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
HtmlTextBlock/TextFragmentStyle.cs
Normal file
32
HtmlTextBlock/TextFragmentStyle.cs
Normal 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
14
HtmlTextBlock/TextLine.cs
Normal 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
140
HtmlTextBlock/TextParser.cs
Normal 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("&", "&");
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
LinkControl/LinkControl.xaml
Normal file
12
LinkControl/LinkControl.xaml
Normal 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>
|
||||
38
LinkControl/LinkControl.xaml.cs
Normal file
38
LinkControl/LinkControl.xaml.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Properties/AssemblyInfo.cs
Normal file
58
Properties/AssemblyInfo.cs
Normal 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")]
|
||||
29
TextListControl/TextList.xaml
Normal file
29
TextListControl/TextList.xaml
Normal 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>
|
||||
95
TextListControl/TextList.xaml.cs
Normal file
95
TextListControl/TextList.xaml.cs
Normal 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
|
||||
}
|
||||
}
|
||||
97
Toolbar/DropDownButton/DropDownButton.cs
Normal file
97
Toolbar/DropDownButton/DropDownButton.cs
Normal 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
|
||||
}
|
||||
}
|
||||
226
Toolbar/DropDownButton/DropDownButtonStyle.xaml
Normal file
226
Toolbar/DropDownButton/DropDownButtonStyle.xaml
Normal 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
25
Toolbar/ImageButton.xaml
Normal 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>
|
||||
30
Toolbar/ImageButton.xaml.cs
Normal file
30
Toolbar/ImageButton.xaml.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
150
Toolbar/SplitButton/SplitButton.cs
Normal file
150
Toolbar/SplitButton/SplitButton.cs
Normal 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
|
||||
}
|
||||
}
|
||||
277
Toolbar/SplitButton/SplitButtonStyle.xaml
Normal file
277
Toolbar/SplitButton/SplitButtonStyle.xaml
Normal 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
178
Windows/ControlBox.cs
Normal 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
324
Windows/SnappingWindow.cs
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user