Add LogIn Form

This commit is contained in:
yaronh
2019-01-28 22:16:14 +08:00
parent 3c3ea19866
commit 060771262f
171 changed files with 19721 additions and 53 deletions
+9 -2
View File
@@ -1,8 +1,15 @@
<Application x:Class="TIDALDL_UI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
StartupUri="Login.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
+68
View File
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TIDALDL_UI
{
public class Config
{
private string Path = AIGS.Helper.SystemHelper.GetExeDirectoryName() + "/Tidal-dl.ini";
private List<AIGS.Common.Property> accounts;
public List<AIGS.Common.Property> Accounts
{
get {
if(accounts == null)
{
accounts = new List<AIGS.Common.Property>();
int count = AIGS.Helper.ConfigHelper.GetValue("num", 0, "account", Path);
for (int i = 0; i < count; i++)
{
string sUser = AIGS.Helper.ConfigHelper.GetValue("user"+i, null, "account", Path);
string sPwd = AIGS.Helper.ConfigHelper.GetValue("pwd"+i, null, "account", Path);
if (string.IsNullOrEmpty(sUser))
continue;
accounts.Add(new AIGS.Common.Property(sUser, sPwd));
}
}
return accounts;
}
set { accounts = value; }
}
public void addAccount(string sUser, string sPwd)
{
for (int i = 0; i < Accounts.Count; i++)
{
if (Accounts[i].Key.ToString() == sUser)
{
Accounts.RemoveAt(i);
break;
}
}
Accounts.Insert(0, new AIGS.Common.Property(sUser, sPwd));
AIGS.Helper.ConfigHelper.SetValue("num", Accounts.Count, "account", Path);
for (int i = 0; i < Accounts.Count; i++)
{
AIGS.Helper.ConfigHelper.SetValue("user"+i, Accounts[i].Key.ToString(), "account", Path);
AIGS.Helper.ConfigHelper.SetValue("pwd" + i, Accounts[i].Value.ToString(), "account", Path);
}
return;
}
public bool Remember
{
get {return AIGS.Helper.ConfigHelper.GetValue("remember", true, "common", Path); }
set { AIGS.Helper.ConfigHelper.SetValue("remember", value, "common", Path); }
}
public bool AutoLogin
{
get { return AIGS.Helper.ConfigHelper.GetValue("autologin", true, "common", Path); }
set { AIGS.Helper.ConfigHelper.SetValue("autologin", value, "common", Path); }
}
}
}
+73
View File
@@ -0,0 +1,73 @@
<Window x:Class="TIDALDL_UI.Login"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:AIG12306"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
TextElement.FontWeight="Regular"
TextElement.FontSize="13"
TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="Auto"
FontFamily="{DynamicResource MaterialDesignFont}"
Title="Tidal-dl"
Width="515"
Height="415" WindowStyle="None" AllowsTransparency="True" Background="{x:Null}">
<Grid>
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth3" VerticalAlignment="Top" HorizontalAlignment="Left" Width="430" BorderThickness="3" BorderBrush="#FF171616" Margin="45,40,0,0" Height="330">
<materialDesign:DialogHost Identifier="RootDialog" SnackbarMessageQueue="{Binding ElementName=MainSnackbar, Path=MessageQueue}">
<Grid Height="329" >
<Grid.RowDefinitions>
<RowDefinition Height="185"/>
<RowDefinition Height="23*"/>
</Grid.RowDefinitions>
<Grid Background="Black" MouseLeftButtonDown="Grid_MouseLeftButtonDown" Margin="0,-10,0,0">
<Image Margin="-10,-10,0,-15" Source="pack://application:,,,/res/tidal.jpg" />
<ToggleButton Style="{StaticResource MaterialDesignFlatToggleButton}"
x:Name="m_CClose"
Margin="404,0,0,160"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
Width="26" Padding="0" Height="25" Click="m_CClose_Click">
<materialDesign:PackIcon Kind="Close" Height="25" Width="26" Foreground="White" />
</ToggleButton>
</Grid>
<Grid Grid.Row="1" Background="White" Name="m_CLoginPage">
<ComboBox Margin="115,10,0,0"
materialDesign:HintAssist.Hint=" UserName"
Background="White"
Name="m_CUser"
IsEditable="True"
VerticalAlignment="Top"
BorderThickness="1"
Height="28"
Width="195"
HorizontalAlignment="Left"
GotFocus="GotFocus"
DisplayMemberPath="Key"
SelectedValuePath="Value"
SelectionChanged="m_CUser_SelectionChanged"/>
<PasswordBox Margin="115,38,0,0"
materialDesign:HintAssist.Hint=" Password"
Background="White"
Name="m_CPwd"
Height="28"
Width="195"
BorderThickness="1,1,1,1" HorizontalAlignment="Left" VerticalAlignment="Top" GotFocus="GotFocus" />
<CheckBox Name="m_CRemember" Content="Remember" Margin="115,66,0,56" IsChecked="True" HorizontalAlignment="Left" Width="90" />
<CheckBox Name="m_CAuto" Content="Auto LongIn" Margin="216,66,0,56" IsChecked="True" HorizontalAlignment="Left" Width="94" Checked="m_CAuto_Checked"/>
<materialDesign:Badged Name="m_CErrLabel" Margin="115,93,0,24">
<Button Name="m_CLogin" Content="LOG IN" HorizontalAlignment="Left" Width="195" Height="Auto" Click="m_CLogin_Click" IsDefault="True"/>
</materialDesign:Badged>
</Grid>
<Grid Grid.Row="1" Background="White" Name="m_CWaitPage" Visibility="Hidden">
<ProgressBar IsIndeterminate="True" Margin="0,30,0,104" Height="Auto"/>
<Button Name="m_CCancle" Content="CANCLE" Margin="115,0,0,24" HorizontalAlignment="Left" Width="195" Height="27" VerticalAlignment="Bottom" Click="m_CCancle_Click"/>
</Grid>
</Grid>
</materialDesign:DialogHost>
</materialDesign:Card>
</Grid>
</Window>
+127
View File
@@ -0,0 +1,127 @@
using MaterialDesignThemes.Wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Tidal;
using AIGS.Helper;
namespace TIDALDL_UI
{
public partial class Login : Window
{
private string UserName;
private string Password;
public Login()
{
InitializeComponent();
this.WindowStartupLocation = WindowStartupLocation.CenterScreen;
m_CUser.ItemsSource = Para.Config.Accounts;
m_CUser.SelectedIndex = 0;
m_CAuto.IsChecked = Para.Config.AutoLogin;
m_CRemember.IsChecked = Para.Config.Remember;
}
private void m_CUser_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (m_CUser.SelectedItem == null)
return;
m_CPwd.Password = ((AIGS.Common.Property)m_CUser.SelectedItem).Value.ToString();
}
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
private void m_CAuto_Checked(object sender, RoutedEventArgs e)
{
m_CRemember.IsChecked = true;
}
private void m_CClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void GotFocus(object sender, RoutedEventArgs e)
{
m_CErrLabel.Badge = "";
}
private void m_CCancle_Click(object sender, RoutedEventArgs e)
{
IsShowWaitPage(false);
}
private void IsShowWaitPage(bool bFlag)
{
m_CLoginPage.Visibility = bFlag ? Visibility.Hidden : Visibility.Visible;
m_CWaitPage.Visibility = bFlag ? Visibility.Visible : Visibility.Hidden;
}
private void m_CLogin_Click(object sender, RoutedEventArgs e)
{
string sUser = m_CUser.Text;
string sPwd = m_CPwd.Password;
if (string.IsNullOrEmpty(sUser) || string.IsNullOrEmpty(sPwd))
{
m_CErrLabel.Badge = "UserName Or Password Err";
return;
}
//show wait page
IsShowWaitPage(true);
//start login thread
this.UserName = sUser;
this.Password = sPwd;
ThreadHelper.Start(ThreadFunc_LogIn);
return;
}
private void ThreadFunc_LogIn(object data)
{
ThreadResultNotify mothed = new ThreadResultNotify(LogInResult);
Account aUser = new Account();
bool bCheck = aUser.LogIn(this.UserName, this.Password);
this.Dispatcher.Invoke(mothed, aUser);
return;
}
delegate void ThreadResultNotify(Account aUser);
private void LogInResult(Account aUser)
{
if(!string.IsNullOrEmpty(aUser.Errmsg))
{
m_CErrLabel.Badge = aUser.Errmsg;
IsShowWaitPage(false);
return;
}
Para.User = aUser;
Tidal.TidalTool.User = aUser;
//add account to config
Para.Config.addAccount(this.UserName, this.Password);
//open main window
MainWindow Form = new MainWindow();
Form.Show();
this.Close();
}
}
}
+23 -2
View File
@@ -1,8 +1,29 @@
<Window x:Class="TIDALDL_UI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
TextElement.FontWeight="Regular"
TextElement.FontSize="13"
TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="Auto"
FontFamily="{DynamicResource MaterialDesignFont}"
Title="Tidal-dl" Height="350" Width="525">
<Grid>
<materialDesign:ColorZone Mode="PrimaryMid" Padding="16">
<DockPanel>
<materialDesign:PopupBox DockPanel.Dock="Right" PlacementMode="BottomAndAlignRightEdges">
<ListBox>
<ListBoxItem>About</ListBoxItem>
</ListBox>
</materialDesign:PopupBox>
<StackPanel Orientation="Horizontal">
<ToggleButton Style="{DynamicResource MaterialDesignHamburgerToggleButton}" />
<TextBlock VerticalAlignment="Center" Margin="16 0 0 0" FontSize="18">
Tidal-DL
</TextBlock>
</StackPanel>
</DockPanel>
</materialDesign:ColorZone>
</Grid>
</Window>
+1
View File
@@ -22,6 +22,7 @@ namespace TIDALDL_UI
public MainWindow()
{
InitializeComponent();
this.WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
}
}
+16
View File
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tidal;
namespace TIDALDL_UI
{
public class Para
{
public static Account User { set; get; }
public static TidalTool Tool { set; get; }
public static Config Config = new Config();
}
}
+22 -30
View File
@@ -1,17 +1,17 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本: 4.0.30319.42000
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将丢失。
// 重新生成代码,这些更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace TIDALDL_UI.Properties
{
namespace TIDALDL_UI.Properties {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
@@ -22,48 +22,40 @@ namespace TIDALDL_UI.Properties
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TIDALDL_UI.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 为所有资源查找重写当前线程的 CurrentUICulture 属性,
/// 方法是使用此强类型资源类
/// 使用此强类型资源类,为所有资源查找
/// 重写当前线程的 CurrentUICulture 属性
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set
{
set {
resourceCulture = value;
}
}
+13 -17
View File
@@ -1,28 +1,24 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace TIDALDL_UI.Properties
{
namespace TIDALDL_UI.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.0.1.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
public static Settings Default {
get {
return defaultInstance;
}
}
+33 -2
View File
@@ -9,10 +9,11 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TIDALDL_UI</RootNamespace>
<AssemblyName>TIDALDL-UI</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -23,6 +24,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -32,8 +34,22 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="AIGS">
<HintPath>packages\AIGS\AIGS.dll</HintPath>
</Reference>
<Reference Include="MaterialDesignColors, Version=1.0.0.14576, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\MaterialDesignColors.1.1.1\lib\net45\MaterialDesignColors.dll</HintPath>
</Reference>
<Reference Include="MaterialDesignThemes.Wpf, Version=2.5.0.1205, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\MaterialDesignThemes.2.5.0.1205\lib\net45\MaterialDesignThemes.Wpf.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>packages\AIGS\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
@@ -53,13 +69,20 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Config.cs" />
<Compile Include="Para.cs" />
<Compile Include="Tidal\Account.cs" />
<Compile Include="Tidal\Album.cs" />
<Compile Include="Tidal\Artist.cs" />
<Compile Include="Tidal\Playlist.cs" />
<Compile Include="Tidal\TidalAccount.cs" />
<Compile Include="Tidal\StreamUrl.cs" />
<Compile Include="Tidal\TidalTool.cs" />
<Compile Include="Tidal\Track.cs" />
<Compile Include="Tidal\Video.cs" />
<Page Include="Login.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@@ -68,6 +91,9 @@
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Login.xaml.cs">
<DependentUpon>Login.xaml</DependentUpon>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
@@ -91,12 +117,17 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="app.config" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<Resource Include="res\tidal.jpg" />
</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.
+107
View File
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AIGS.Helper;
namespace Tidal
{
public class Account
{
private static string Url = "https://api.tidalhifi.com/v1/";
private static string Token = "4zx46pyr9o8qZNRw";
private static string Version = "1.9.1";
public string Errmsg = "";
private string user;
public string User
{
get { return user; }
set { user = value; }
}
private string userid;
public string UserID
{
get { return userid; }
set { userid = value; }
}
private string pwd;
public string Pwd
{
get { return pwd; }
set { pwd = value; }
}
private string sessionid;
public string SessionID
{
get { return sessionid; }
set { sessionid = value; }
}
private string countrycode;
public string CountryCode
{
get { return countrycode; }
set { countrycode = value; }
}
private string GetUID()
{
string sRet = "";
string[] sArray = Guid.NewGuid().ToString().Split('-');
for (int i = 0; i < sArray.Count(); i++)
{
sRet += sArray[i];
}
return sRet;
}
public bool LogIn(string sUser, string sPwd)
{
Errmsg = "";
Dictionary<string,string>pPara = new Dictionary<string, string>()
{
{"username", sUser},
{"password", sPwd},
{"token", Token},
{"clientVersion", Version},
{"clientUniqueKey", GetUID()}
};
string sRet = (string)HttpHelper.GetOrPost(Url+ "login/username", pPara);
if (string.IsNullOrEmpty(sRet))
{
if (!string.IsNullOrEmpty(HttpHelper.Errmsg))
{
if (HttpHelper.Errmsg.Contains("401"))
Errmsg = "Uername or password err!";
else
Errmsg = "Get sessionid err!";
return false;
}
}
string sUserID = JsonHelper.GetValue(sRet, "userId");
string sSessionID = JsonHelper.GetValue(sRet, "sessionId");
string sCountryCode = JsonHelper.GetValue(sRet, "countryCode");
if(string.IsNullOrEmpty(sUserID) || string.IsNullOrEmpty(sSessionID) || string.IsNullOrEmpty(sCountryCode))
{
Errmsg = "Get sessionid/userId/countryCode err!";
return false;
}
string sRet2 = (string)HttpHelper.GetOrPost(Url + "users/" + sUserID, new Dictionary<string, string>() { { "sessionId", sSessionID } });
string sStatus2 = JsonHelper.GetValue(sRet2, "status");
if (!string.IsNullOrEmpty(sStatus2) && sStatus2 != "200")
{
Errmsg = "Sessionid is unvalid!";
return false;
}
this.User = sUser;
this.Pwd = sPwd;
this.SessionID = sSessionID;
this.UserID = sUserID;
this.CountryCode = sCountryCode;
return true;
}
}
}
+88
View File
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Tidal
{
public class Album
{
//专辑ID
private int id;
public int ID
{
get { return id; }
set { id = value; }
}
//专辑名称
private string title;
public string Title
{
get { return title; }
set { title = value; }
}
//专辑发行日期
private string releasedate;
public string ReleaseDate
{
get { return releasedate; }
set { releasedate = value; }
}
//专辑版权所属
private string copyright;
public string CopyRight
{
get { return copyright; }
set { copyright = value; }
}
//专辑时长
private int duration;
public int Duration
{
get { return duration; }
set { duration = value; }
}
//专辑封面编码
private string cover;
public string Cover
{
get { return cover; }
set { cover = value; }
}
//专辑封面链接
private string convrurl;
public string ConvrUrl
{
get { return convrurl; }
set { convrurl = value; }
}
//专辑曲目数量
private int numberoftracks;
public int NumberOfTracks
{
get { return numberoftracks; }
set { numberoftracks = value; }
}
//专辑视频数量
private int numberofvideos;
public int NumberOfVideos
{
get { return numberofvideos; }
set { numberofvideos = value; }
}
//专辑碟数量
private int numberofvolumes;
public int NumberOfVolumes
{
get { return numberofvolumes; }
set { numberofvolumes = value; }
}
//歌手
private Artist artist;
public Artist Artist
{
get { return artist; }
set { artist = value; }
}
}
}
+54
View File
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Tidal
{
public class Artist
{
//艺人ID
private int id;
public int ID
{
get { return id; }
set { id = value; }
}
//艺人名称
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
//连接
private string url;
public string Url
{
get { return url; }
set { url = value; }
}
//图片
private string pricture;
public string Picture
{
get { return pricture; }
set { pricture = value; }
}
//图片链接
private string prictureurl;
public string PictureUrl
{
get { return prictureurl; }
set { prictureurl = value; }
}
//热度
private int popularity;
public int Popularity
{
get { return popularity; }
set { popularity = value; }
}
}
}
+88
View File
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Tidal
{
public class Playlist
{
private string uuid;
public string Uuid
{
get { return uuid; }
set { uuid = value; }
}
private string title;
public string Title
{
get { return title; }
set { title = value; }
}
private int numberoftracks;
public int NumberOfTracks
{
get { return numberoftracks; }
set { numberoftracks = value; }
}
private int numberofvideos;
public int NumberOfVideos
{
get { return numberofvideos; }
set { numberofvideos = value; }
}
private string description;
public string Description
{
get { return description; }
set { description = value; }
}
private int duration;
public int Duration
{
get { return duration; }
set { duration = value; }
}
private string lastupdated;
public string LastUpdated
{
get { return lastupdated; }
set { lastupdated = value; }
}
private string created;
public string Created
{
get { return created; }
set { created = value; }
}
private string type;
public string Type
{
get { return type; }
set { type = value; }
}
private string url;
public string Url
{
get { return url; }
set { url = value; }
}
private string image;
public string Image
{
get { return image; }
set { image = value; }
}
}
}
+54
View File
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tidal
{
public class StreamUrl
{
private string codec;
public string Codec
{
get { return codec; }
set { codec = value; }
}
private string encryptionKey;
public string EncryptionKey
{
get { return encryptionKey; }
set { encryptionKey = value; }
}
private int playTimeLeftInMinutes;
public int PlayTimeLeftInMinutes
{
get { return playTimeLeftInMinutes; }
set { playTimeLeftInMinutes = value; }
}
private string soundQuality;
public string SoundQuality
{
get { return soundQuality; }
set { soundQuality = value; }
}
private int trackId;
public int TrackId
{
get { return trackId; }
set { trackId = value; }
}
private string url;
public string Url
{
get { return url; }
set { url = value; }
}
}
}
+75
View File
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AIGS.Helper;
namespace Tidal
{
public class TidalTool
{
public static Account User;
public static string Errmsg;
private static string Url = "https://api.tidalhifi.com/v1/";
private static string Get(string Path, Dictionary<string, string> Paras = null)
{
string sParams = "?countryCode=" + User.CountryCode;
for (int i = 0; Paras != null && i < Paras.Count; i++)
{
sParams += "&" + Paras.ElementAt(i).Key + "=" + Paras.ElementAt(i).Value;
}
string sRet = (string)HttpHelper.GetOrPost(Url + Path + sParams, Header: "X-Tidal-SessionId:" + User.SessionID);
string sStatus = JsonHelper.GetValue(sRet, "status");
string sSubStatus = JsonHelper.GetValue(sRet, "subStatus");
if (!string.IsNullOrEmpty(sStatus) && sStatus == "404" && sSubStatus == "2001")
Errmsg = JsonHelper.GetValue(sRet, "userMessage") + ". This might be region-locked.";
if (!string.IsNullOrEmpty(sStatus) && sStatus != "200")
Errmsg = "Get operation err!";
return sRet;
}
public static Album GetAlbum(string sID)
{
string sRet = Get("albums/" + sID);
if (string.IsNullOrEmpty(sRet) || !string.IsNullOrEmpty(Errmsg))
return null;
Album aRet = JsonHelper.ConverStringToObject<Album>(sRet);
return aRet;
}
public static List<Track> GetAlbumTracks(string sID)
{
string sRet = Get("albums/" + sID + "/tracks");
if (string.IsNullOrEmpty(sRet) || !string.IsNullOrEmpty(Errmsg))
return null;
List<Track> aRet = JsonHelper.ConverStringToObject<List<Track>>(sRet, "items");
return aRet;
}
public static Track GetTrack(string sID)
{
string sRet = Get("tracks/" + sID);
if (string.IsNullOrEmpty(sRet) || !string.IsNullOrEmpty(Errmsg))
return null;
Track aRet = JsonHelper.ConverStringToObject<Track>(sRet);
return aRet;
}
public static StreamUrl GetStreamUrl(string sID, string sQuality)
{
string sRet = Get("tracks/" + sID + "/streamUrl", new Dictionary<string, string>() { { "soundQuality", sQuality } });
if (string.IsNullOrEmpty(sRet) || !string.IsNullOrEmpty(Errmsg))
return null;
StreamUrl aRet = JsonHelper.ConverStringToObject<StreamUrl>(sRet);
return aRet;
}
}
}
+74
View File
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Tidal
{
public class Track
{
//曲目ID
private int id;
public int ID
{
get { return id; }
set { id = value; }
}
//曲目名称
private string title;
public string Title
{
get { return title; }
set { title = value; }
}
//曲目时长(单位秒)
private int duration;
public int Duration
{
get { return duration; }
set { duration = value; }
}
//曲目序号
private int tracknumber;
public int TrackNumber
{
get { return tracknumber; }
set { tracknumber = value; }
}
//曲目所属碟
private int volumenumber;
public int VolumeNumber
{
get { return volumenumber; }
set { volumenumber = value; }
}
//曲目版本
private string version;
public string Version
{
get { return version; }
set { version = value; }
}
//专辑版权所属
private string copyright;
public string CopyRight
{
get { return copyright; }
set { copyright = value; }
}
//歌手
private Artist artist;
public Artist Artist
{
get { return artist; }
set { artist = value; }
}
//专辑
private Album album;
public Album Album
{
get { return album; }
set { album = value; }
}
}
}
+69
View File
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Tidal
{
public class Video
{
private int id;
public int ID
{
get { return id; }
set { id = value; }
}
private string title;
public string Title
{
get { return title; }
set { title = value; }
}
private int duration;
public int Duration
{
get { return duration; }
set { duration = value; }
}
private int tracknumber;
public int TrackNumber
{
get { return tracknumber; }
set { tracknumber = value; }
}
private int releasedate;
public int ReleaseDate
{
get { return releasedate; }
set { releasedate = value; }
}
//曲目版本
private string version;
public string Version
{
get { return version; }
set { version = value; }
}
//专辑版权所属
private string copyright;
public string CopyRight
{
get { return copyright; }
set { copyright = value; }
}
//歌手
private Artist artist;
public Artist Artist
{
get { return artist; }
set { artist = value; }
}
//专辑
private Album album;
public Album Album
{
get { return album; }
set { album = value; }
}
}
}
+3
View File
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MaterialDesignColors" version="1.1.1" targetFramework="net45" />
<package id="MaterialDesignThemes" version="2.5.0.1205" targetFramework="net45" />
</packages>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,64 @@
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media.Animation;
using ControlzEx;
namespace MaterialDesignThemes.Wpf
{
[TemplatePart(Name = BadgeContainerPartName, Type = typeof(UIElement))]
public class Badged : BadgedEx
{
public static readonly DependencyProperty BadgeChangedStoryboardProperty = DependencyProperty.Register(
"BadgeChangedStoryboard", typeof(Storyboard), typeof(Badged), new PropertyMetadata(default(Storyboard)));
public Storyboard BadgeChangedStoryboard
{
get { return (Storyboard)this.GetValue(BadgeChangedStoryboardProperty); }
set { this.SetValue(BadgeChangedStoryboardProperty, value); }
}
static Badged()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Badged), new FrameworkPropertyMetadata(typeof(Badged)));
}
public static readonly DependencyProperty BadgeColorZoneModeProperty = DependencyProperty.Register(
"BadgeColorZoneMode", typeof(ColorZoneMode), typeof(Badged), new PropertyMetadata(default(ColorZoneMode)));
public ColorZoneMode BadgeColorZoneMode
{
get { return (ColorZoneMode) GetValue(BadgeColorZoneModeProperty); }
set { SetValue(BadgeColorZoneModeProperty, value); }
}
public override void OnApplyTemplate()
{
this.BadgeChanged -= this.OnBadgeChanged;
base.OnApplyTemplate();
this.BadgeChanged += this.OnBadgeChanged;
}
private void OnBadgeChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var sb = this.BadgeChangedStoryboard;
if (this._badgeContainer != null && sb != null)
{
try
{
this._badgeContainer.BeginStoryboard(sb);
}
catch (Exception exc)
{
Trace.WriteLine("Error during Storyboard execution, exception will be rethrown.");
Trace.WriteLine($"{exc.Message} ({exc.GetType().FullName})");
Trace.WriteLine(exc.StackTrace);
throw;
}
}
}
}
}
@@ -0,0 +1,9 @@
namespace MaterialDesignThemes.Wpf
{
public enum BaseTheme
{
Inherit,
Light,
Dark
}
}
@@ -0,0 +1,112 @@
using System.Windows;
using System.Windows.Media;
namespace MaterialDesignThemes.Wpf
{
public static class ButtonProgressAssist
{
public static readonly DependencyProperty MinimumProperty = DependencyProperty.RegisterAttached(
"Minimum", typeof(double), typeof(ButtonProgressAssist), new FrameworkPropertyMetadata(default(double)));
public static void SetMinimum(DependencyObject element, double value)
{
element.SetValue(MinimumProperty, value);
}
public static double GetMinimum(DependencyObject element)
{
return (double)element.GetValue(MinimumProperty);
}
public static readonly DependencyProperty MaximumProperty = DependencyProperty.RegisterAttached(
"Maximum", typeof(double), typeof(ButtonProgressAssist), new FrameworkPropertyMetadata(100.0));
public static void SetMaximum(DependencyObject element, double value)
{
element.SetValue(MaximumProperty, value);
}
public static double GetMaximum(DependencyObject element)
{
return (double)element.GetValue(MaximumProperty);
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(
"Value", typeof(double), typeof(ButtonProgressAssist), new FrameworkPropertyMetadata(default(double)));
public static void SetValue(DependencyObject element, double value)
{
element.SetValue(ValueProperty, value);
}
public static double GetValue(DependencyObject element)
{
return (double)element.GetValue(ValueProperty);
}
public static readonly DependencyProperty IsIndeterminateProperty = DependencyProperty.RegisterAttached(
"IsIndeterminate", typeof(bool), typeof(ButtonProgressAssist), new FrameworkPropertyMetadata(default(bool)));
public static void SetIsIndeterminate(DependencyObject element, bool isIndeterminate)
{
element.SetValue(IsIndeterminateProperty, isIndeterminate);
}
public static bool GetIsIndeterminate(DependencyObject element)
{
return (bool)element.GetValue(IndicatorForegroundProperty);
}
public static readonly DependencyProperty IndicatorForegroundProperty = DependencyProperty.RegisterAttached(
"IndicatorForeground", typeof(Brush), typeof(ButtonProgressAssist), new FrameworkPropertyMetadata(default(Brush)));
public static void SetIndicatorForeground(DependencyObject element, Brush indicatorForeground)
{
element.SetValue(IndicatorForegroundProperty, indicatorForeground);
}
public static Brush GetIndicatorForeground(DependencyObject element)
{
return (Brush)element.GetValue(IndicatorForegroundProperty);
}
public static readonly DependencyProperty IndicatorBackgroundProperty = DependencyProperty.RegisterAttached(
"IndicatorBackground", typeof(Brush), typeof(ButtonProgressAssist), new FrameworkPropertyMetadata(default(Brush)));
public static void SetIndicatorBackground(DependencyObject element, Brush indicatorBackground)
{
element.SetValue(IndicatorBackgroundProperty, indicatorBackground);
}
public static Brush GetIndicatorBackground(DependencyObject element)
{
return (Brush)element.GetValue(IndicatorForegroundProperty);
}
public static readonly DependencyProperty IsIndicatorVisibleProperty = DependencyProperty.RegisterAttached(
"IsIndicatorVisible", typeof(bool), typeof(ButtonProgressAssist), new FrameworkPropertyMetadata(default(bool)));
public static void SetIsIndicatorVisible(DependencyObject element, bool isIndicatorVisible)
{
element.SetValue(IsIndicatorVisibleProperty, isIndicatorVisible);
}
public static bool GetIsIndicatorVisible(DependencyObject element)
{
return (bool)element.GetValue(IsIndicatorVisibleProperty);
}
public static readonly DependencyProperty OpacityProperty = DependencyProperty.RegisterAttached(
"Opacity", typeof(double), typeof(ButtonProgressAssist), new FrameworkPropertyMetadata(default(double)));
public static void SetOpacity(DependencyObject element, double opacity)
{
element.SetValue(OpacityProperty, opacity);
}
public static double GetOpacity(DependencyObject element)
{
return (double)element.GetValue(OpacityProperty);
}
}
}
@@ -0,0 +1,70 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace MaterialDesignThemes.Wpf
{
/// <summary>
/// A card is a content control, styled according to Material Design guidelines.
/// </summary>
[TemplatePart(Name = ClipBorderPartName, Type = typeof(Border))]
public class Card : ContentControl
{
public const string ClipBorderPartName = "PART_ClipBorder";
private Border _clipBorder;
static Card()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Card), new FrameworkPropertyMetadata(typeof(Card)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_clipBorder = Template.FindName(ClipBorderPartName, this) as Border;
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
if (_clipBorder == null) return;
var farPoint = new Point(
Math.Max(0, _clipBorder.ActualWidth),
Math.Max(0, _clipBorder.ActualHeight));
var clipRect = new Rect(
new Point(),
new Point(farPoint.X, farPoint.Y));
ContentClip = new RectangleGeometry(clipRect, UniformCornerRadius, UniformCornerRadius);
}
public static readonly DependencyProperty UniformCornerRadiusProperty = DependencyProperty.Register(
nameof(UniformCornerRadius), typeof (double), typeof (Card), new FrameworkPropertyMetadata(2.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double UniformCornerRadius
{
get { return (double) GetValue(UniformCornerRadiusProperty); }
set { SetValue(UniformCornerRadiusProperty, value); }
}
private static readonly DependencyPropertyKey ContentClipPropertyKey =
DependencyProperty.RegisterReadOnly(
"ContentClip", typeof (Geometry), typeof (Card),
new PropertyMetadata(default(Geometry)));
public static readonly DependencyProperty ContentClipProperty =
ContentClipPropertyKey.DependencyProperty;
public Geometry ContentClip
{
get { return (Geometry) GetValue(ContentClipProperty); }
private set { SetValue(ContentClipPropertyKey, value); }
}
}
}
@@ -0,0 +1,127 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
namespace MaterialDesignThemes.Wpf
{
[TemplatePart(Name = DeleteButtonPartName, Type = typeof(Button))]
public class Chip : ButtonBase
{
private ButtonBase _deleteButton;
public const string DeleteButtonPartName = "PART_DeleteButton";
static Chip()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Chip), new FrameworkPropertyMetadata(typeof(Chip)));
}
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
"Icon", typeof (object), typeof (Chip), new PropertyMetadata(default(object)));
public object Icon
{
get { return (object) GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconBackgroundProperty = DependencyProperty.Register(
"IconBackground", typeof (Brush), typeof (Chip), new PropertyMetadata(default(Brush)));
public Brush IconBackground
{
get { return (Brush) GetValue(IconBackgroundProperty); }
set { SetValue(IconBackgroundProperty, value); }
}
public static readonly DependencyProperty IconForegroundProperty = DependencyProperty.Register(
"IconForeground", typeof (Brush), typeof (Chip), new PropertyMetadata(default(Brush)));
public Brush IconForeground
{
get { return (Brush) GetValue(IconForegroundProperty); }
set { SetValue(IconForegroundProperty, value); }
}
public static readonly DependencyProperty IsDeletableProperty = DependencyProperty.Register(
"IsDeletable", typeof (bool), typeof (Chip), new PropertyMetadata(default(bool)));
/// <summary>
/// Indicates if the delete button should be visible.
/// </summary>
public bool IsDeletable
{
get { return (bool) GetValue(IsDeletableProperty); }
set { SetValue(IsDeletableProperty, value); }
}
public static readonly DependencyProperty DeleteCommandProperty = DependencyProperty.Register(
"DeleteCommand", typeof (ICommand), typeof (Chip), new PropertyMetadata(default(ICommand)));
public ICommand DeleteCommand
{
get { return (ICommand) GetValue(DeleteCommandProperty); }
set { SetValue(DeleteCommandProperty, value); }
}
public static readonly DependencyProperty DeleteCommandParameterProperty = DependencyProperty.Register(
"DeleteCommandParameter", typeof (object), typeof (Chip), new PropertyMetadata(default(object)));
public object DeleteCommandParameter
{
get { return (object) GetValue(DeleteCommandParameterProperty); }
set { SetValue(DeleteCommandParameterProperty, value); }
}
public static readonly DependencyProperty DeleteToolTipProperty = DependencyProperty.Register(
"DeleteToolTip", typeof (object), typeof (Chip), new PropertyMetadata(default(object)));
public object DeleteToolTip
{
get { return (object) GetValue(DeleteToolTipProperty); }
set { SetValue(DeleteToolTipProperty, value); }
}
/// <summary>
/// Event correspond to delete button left mouse button click
/// </summary>
public static readonly RoutedEvent DeleteClickEvent = EventManager.RegisterRoutedEvent("DeleteClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Chip));
/// <summary>
/// Add / Remove DeleteClickEvent handler
/// </summary>
[Category("Behavior")]
public event RoutedEventHandler DeleteClick { add { AddHandler(DeleteClickEvent, value); } remove { RemoveHandler(DeleteClickEvent, value); } }
public override void OnApplyTemplate()
{
if (_deleteButton != null)
_deleteButton.Click -= DeleteButtonOnClick;
_deleteButton = GetTemplateChild(DeleteButtonPartName) as ButtonBase;
if (_deleteButton != null)
_deleteButton.Click += DeleteButtonOnClick;
base.OnApplyTemplate();
}
protected virtual void OnDeleteClick()
{
var newEvent = new RoutedEventArgs(DeleteClickEvent, this);
RaiseEvent(newEvent);
var command = DeleteCommand;
if (command != null && command.CanExecute(DeleteCommandParameter))
command.Execute(DeleteCommandParameter);
}
private void DeleteButtonOnClick(object sender, RoutedEventArgs routedEventArgs)
{
OnDeleteClick();
routedEventArgs.Handled = true;
}
}
}
@@ -0,0 +1,461 @@
using MaterialDesignThemes.Wpf.Converters;
using MaterialDesignThemes.Wpf.Transitions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
namespace MaterialDesignThemes.Wpf
{
public enum ClockDisplayMode
{
Hours,
Minutes,
Seconds,
}
public enum ClockDisplayAutomation
{
None,
Cycle,
ToMinutesOnly,
ToSeconds,
CycleWithSeconds,
}
[TemplatePart(Name = HoursCanvasPartName, Type = typeof(Canvas))]
[TemplatePart(Name = MinutesCanvasPartName, Type = typeof(Canvas))]
[TemplatePart(Name = SecondsCanvasPartName, Type = typeof(Canvas))]
[TemplatePart(Name = MinuteReadOutPartName, Type = typeof(TextBlock))]
[TemplatePart(Name = HourReadOutPartName, Type = typeof(TextBlock))]
[TemplateVisualState(GroupName = "DisplayModeStates", Name = HoursVisualStateName)]
[TemplateVisualState(GroupName = "DisplayModeStates", Name = MinutesVisualStateName)]
public class Clock : Control
{
public const string HoursCanvasPartName = "PART_HoursCanvas";
public const string MinutesCanvasPartName = "PART_MinutesCanvas";
public const string SecondsCanvasPartName = "PART_SecondsCanvas";
public const string MinuteReadOutPartName = "PART_MinuteReadOut";
public const string SecondReadOutPartName = "PART_SecondReadOut";
public const string HourReadOutPartName = "PART_HourReadOut";
public const string HoursVisualStateName = "Hours";
public const string MinutesVisualStateName = "Minutes";
public const string SecondsVisualStateName = "Seconds";
private Point _centreCanvas = new Point(0, 0);
private Point _currentStartPosition = new Point(0, 0);
private TextBlock _hourReadOutPartName;
private TextBlock _minuteReadOutPartName;
private TextBlock _secondReadOutPartName;
static Clock()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Clock), new FrameworkPropertyMetadata(typeof(Clock)));
}
public Clock()
{
AddHandler(ClockItemButton.DragStartedEvent, new DragStartedEventHandler(ClockItemDragStartedHandler));
AddHandler(ClockItemButton.DragDeltaEvent, new DragDeltaEventHandler(ClockItemDragDeltaHandler));
AddHandler(ClockItemButton.DragCompletedEvent, new DragCompletedEventHandler(ClockItemDragCompletedHandler));
}
public static readonly DependencyProperty TimeProperty = DependencyProperty.Register(
nameof(Time), typeof(DateTime), typeof(Clock), new FrameworkPropertyMetadata(default(DateTime), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, TimePropertyChangedCallback));
private static void TimePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var clock = (Clock)dependencyObject;
SetFlags(clock);
}
public DateTime Time
{
get { return (DateTime)GetValue(TimeProperty); }
set { SetValue(TimeProperty, value); }
}
private static readonly DependencyPropertyKey IsMidnightHourPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsMidnightHour", typeof(bool), typeof(Clock),
new PropertyMetadata(default(bool)));
public static readonly DependencyProperty IsMidnightHourProperty =
IsMidnightHourPropertyKey.DependencyProperty;
public bool IsMidnightHour
{
get { return (bool)GetValue(IsMidnightHourProperty); }
private set { SetValue(IsMidnightHourPropertyKey, value); }
}
private static readonly DependencyPropertyKey IsMiddayHourPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsMiddayHour", typeof(bool), typeof(Clock),
new PropertyMetadata(default(bool)));
public static readonly DependencyProperty IsMiddayHourProperty =
IsMiddayHourPropertyKey.DependencyProperty;
public bool IsMiddayHour
{
get { return (bool)GetValue(IsMiddayHourProperty); }
private set { SetValue(IsMiddayHourPropertyKey, value); }
}
public static readonly DependencyProperty IsPostMeridiemProperty = DependencyProperty.Register(
nameof(IsPostMeridiem), typeof(bool), typeof(Clock), new PropertyMetadata(default(bool), IsPostMeridiemPropertyChangedCallback));
private static void IsPostMeridiemPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var clock = (Clock)dependencyObject;
if (clock.IsPostMeridiem && clock.Time.Hour < 12)
clock.Time = new DateTime(clock.Time.Year, clock.Time.Month, clock.Time.Day, clock.Time.Hour + 12, clock.Time.Minute, clock.Time.Second);
else if (!clock.IsPostMeridiem && clock.Time.Hour >= 12)
clock.Time = new DateTime(clock.Time.Year, clock.Time.Month, clock.Time.Day, clock.Time.Hour - 12, clock.Time.Minute, clock.Time.Second);
}
public bool IsPostMeridiem
{
get { return (bool)GetValue(IsPostMeridiemProperty); }
set { SetValue(IsPostMeridiemProperty, value); }
}
public static readonly DependencyProperty Is24HoursProperty = DependencyProperty.Register(
nameof(Is24Hours), typeof(bool), typeof(Clock), new PropertyMetadata(default(bool), Is24HoursChanged));
private static void Is24HoursChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Clock)d).GenerateButtons();
}
public bool Is24Hours
{
get { return (bool)GetValue(Is24HoursProperty); }
set { SetValue(Is24HoursProperty, value); }
}
public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register(
nameof(DisplayMode), typeof(ClockDisplayMode), typeof(Clock), new FrameworkPropertyMetadata(ClockDisplayMode.Hours, DisplayModePropertyChangedCallback));
private static void DisplayModePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
((Clock)dependencyObject).GotoVisualState(!TransitionAssist.GetDisableTransitions(dependencyObject));
}
public ClockDisplayMode DisplayMode
{
get { return (ClockDisplayMode)GetValue(DisplayModeProperty); }
set { SetValue(DisplayModeProperty, value); }
}
public static readonly DependencyProperty DisplayAutomationProperty = DependencyProperty.Register(
nameof(DisplayAutomation), typeof(ClockDisplayAutomation), typeof(Clock), new PropertyMetadata(default(ClockDisplayAutomation)));
public ClockDisplayAutomation DisplayAutomation
{
get { return (ClockDisplayAutomation)GetValue(DisplayAutomationProperty); }
set { SetValue(DisplayAutomationProperty, value); }
}
public static readonly DependencyProperty ButtonStyleProperty = DependencyProperty.Register(
nameof(ButtonStyle), typeof(Style), typeof(Clock), new PropertyMetadata(default(Style)));
public Style ButtonStyle
{
get { return (Style)GetValue(ButtonStyleProperty); }
set { SetValue(ButtonStyleProperty, value); }
}
public static readonly DependencyProperty LesserButtonStyleProperty = DependencyProperty.Register(
nameof(LesserButtonStyle), typeof(Style), typeof(Clock), new PropertyMetadata(default(Style)));
public Style LesserButtonStyle
{
get { return (Style)GetValue(LesserButtonStyleProperty); }
set { SetValue(LesserButtonStyleProperty, value); }
}
public static readonly DependencyProperty ButtonRadiusRatioProperty = DependencyProperty.Register(
nameof(ButtonRadiusRatio), typeof(double), typeof(Clock), new PropertyMetadata(default(double)));
public double ButtonRadiusRatio
{
get { return (double)GetValue(ButtonRadiusRatioProperty); }
set { SetValue(ButtonRadiusRatioProperty, value); }
}
public static readonly DependencyProperty ButtonRadiusInnerRatioProperty = DependencyProperty.Register(
nameof(ButtonRadiusInnerRatio), typeof(double), typeof(Clock), new PropertyMetadata(default(double)));
public double ButtonRadiusInnerRatio
{
get { return (double)GetValue(ButtonRadiusInnerRatioProperty); }
set { SetValue(ButtonRadiusInnerRatioProperty, value); }
}
private static readonly DependencyPropertyKey HourLineAnglePropertyKey =
DependencyProperty.RegisterReadOnly(
"HourLineAngle", typeof(double), typeof(Clock),
new PropertyMetadata(default(double)));
public static readonly DependencyProperty HourLineAngleProperty =
HourLineAnglePropertyKey.DependencyProperty;
public double HourLineAngle
{
get { return (double)GetValue(HourLineAngleProperty); }
private set { SetValue(HourLineAnglePropertyKey, value); }
}
public static readonly RoutedEvent ClockChoiceMadeEvent =
EventManager.RegisterRoutedEvent(
"ClockChoiceMade",
RoutingStrategy.Bubble,
typeof(ClockChoiceMadeEventHandler),
typeof(Clock));
private static void OnClockChoiceMade(DependencyObject d, ClockDisplayMode displayMode)
{
var instance = (Clock)d;
var dragCompletedEventArgs = new ClockChoiceMadeEventArgs(displayMode)
{
RoutedEvent = ClockChoiceMadeEvent,
};
instance.RaiseEvent(dragCompletedEventArgs);
}
public override void OnApplyTemplate()
{
SetFlags(this);
GenerateButtons();
if (_hourReadOutPartName != null)
_hourReadOutPartName.PreviewMouseLeftButtonDown -= HourReadOutPartNameOnPreviewMouseLeftButtonDown;
if (_minuteReadOutPartName != null)
_minuteReadOutPartName.PreviewMouseLeftButtonDown -= MinuteReadOutPartNameOnPreviewMouseLeftButtonDown;
if (_secondReadOutPartName != null)
_secondReadOutPartName.PreviewMouseLeftButtonDown -= SecondReadOutPartNameOnPreviewMouseLeftButtonDown;
_hourReadOutPartName = GetTemplateChild(HourReadOutPartName) as TextBlock;
_minuteReadOutPartName = GetTemplateChild(MinuteReadOutPartName) as TextBlock;
_secondReadOutPartName = GetTemplateChild(SecondReadOutPartName) as TextBlock;
if (_hourReadOutPartName != null)
_hourReadOutPartName.PreviewMouseLeftButtonDown += HourReadOutPartNameOnPreviewMouseLeftButtonDown;
if (_minuteReadOutPartName != null)
_minuteReadOutPartName.PreviewMouseLeftButtonDown += MinuteReadOutPartNameOnPreviewMouseLeftButtonDown;
if (_secondReadOutPartName != null)
_secondReadOutPartName.PreviewMouseLeftButtonDown += SecondReadOutPartNameOnPreviewMouseLeftButtonDown;
base.OnApplyTemplate();
GotoVisualState(false);
}
private void GotoVisualState(bool useTransitions)
{
VisualStateManager.GoToState(this,
DisplayMode == ClockDisplayMode.Hours
? HoursVisualStateName
: DisplayMode == ClockDisplayMode.Minutes
? MinutesVisualStateName
: SecondsVisualStateName, useTransitions);
}
private void GenerateButtons()
{
if (GetTemplateChild(HoursCanvasPartName) is Canvas hoursCanvas)
{
RemoveExistingButtons(hoursCanvas);
if (Is24Hours)
{
GenerateButtons(hoursCanvas, Enumerable.Range(13, 12).ToList(), ButtonRadiusRatio,
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Hours, Is24Hours), i => "ButtonStyle", "00");
GenerateButtons(hoursCanvas, Enumerable.Range(1, 12).ToList(), ButtonRadiusInnerRatio,
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Hours, Is24Hours), i => "ButtonStyle", "#");
}
else
GenerateButtons(hoursCanvas, Enumerable.Range(1, 12).ToList(), ButtonRadiusRatio,
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Hours, Is24Hours), i => "ButtonStyle", "0");
}
if (GetTemplateChild(MinutesCanvasPartName) is Canvas minutesCanvas)
{
RemoveExistingButtons(minutesCanvas);
GenerateButtons(minutesCanvas, Enumerable.Range(1, 60).ToList(), ButtonRadiusRatio,
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Minutes, Is24Hours),
i => ((i / 5.0) % 1) == 0.0 ? "ButtonStyle" : "LesserButtonStyle", "0");
}
if (GetTemplateChild(SecondsCanvasPartName) is Canvas secondsCanvas)
{
RemoveExistingButtons(secondsCanvas);
GenerateButtons(secondsCanvas, Enumerable.Range(1, 60).ToList(), ButtonRadiusRatio,
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Seconds, Is24Hours),
i => ((i / 5.0) % 1) == 0.0 ? "ButtonStyle" : "LesserButtonStyle", "0");
}
void RemoveExistingButtons(Canvas canvas)
{
for (int i = canvas.Children.Count - 1; i >= 0; i--)
{
if (canvas.Children[i] is ClockItemButton)
{
canvas.Children.RemoveAt(i);
}
}
}
}
private void SecondReadOutPartNameOnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
SetCurrentValue(Clock.DisplayModeProperty, ClockDisplayMode.Seconds);
}
private void MinuteReadOutPartNameOnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
SetCurrentValue(Clock.DisplayModeProperty, ClockDisplayMode.Minutes);
}
private void HourReadOutPartNameOnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
SetCurrentValue(Clock.DisplayModeProperty, ClockDisplayMode.Hours);
}
private void GenerateButtons(Panel canvas, ICollection<int> range, double radiusRatio, IValueConverter isCheckedConverter, Func<int, string> stylePropertySelector,
string format)
{
var anglePerItem = 360.0 / range.Count;
var radiansPerItem = anglePerItem * (Math.PI / 180);
//nothing fancy with sizing/measuring...we are demanding a height
if (canvas.Width < 10.0 || Math.Abs(canvas.Height - canvas.Width) > 0.0) return;
_centreCanvas = new Point(canvas.Width / 2, canvas.Height / 2);
var hypotenuseRadius = _centreCanvas.X * radiusRatio;
foreach (var i in range)
{
var button = new ClockItemButton();
button.SetBinding(StyleProperty, GetBinding(stylePropertySelector(i)));
var adjacent = Math.Cos(i * radiansPerItem) * hypotenuseRadius;
var opposite = Math.Sin(i * radiansPerItem) * hypotenuseRadius;
button.CentreX = _centreCanvas.X + opposite;
button.CentreY = _centreCanvas.Y - adjacent;
button.SetBinding(ToggleButton.IsCheckedProperty, GetBinding("Time", converter: isCheckedConverter, converterParameter: i));
button.SetBinding(Canvas.LeftProperty, GetBinding("X", button));
button.SetBinding(Canvas.TopProperty, GetBinding("Y", button));
button.Content = (i == 60 ? 0 : (i == 24 ? 0 : i)).ToString(format);
canvas.Children.Add(button);
}
}
private void ClockItemDragCompletedHandler(object sender, DragCompletedEventArgs e)
{
OnClockChoiceMade(this, DisplayMode);
switch (DisplayAutomation)
{
case ClockDisplayAutomation.None:
break;
case ClockDisplayAutomation.Cycle:
DisplayMode = DisplayMode == ClockDisplayMode.Hours ? ClockDisplayMode.Minutes : ClockDisplayMode.Hours;
break;
case ClockDisplayAutomation.CycleWithSeconds:
if (DisplayMode == ClockDisplayMode.Hours)
DisplayMode = ClockDisplayMode.Minutes;
else if (DisplayMode == ClockDisplayMode.Minutes)
DisplayMode = ClockDisplayMode.Seconds;
else
DisplayMode = ClockDisplayMode.Hours;
break;
case ClockDisplayAutomation.ToMinutesOnly:
if (DisplayMode == ClockDisplayMode.Hours)
DisplayMode = ClockDisplayMode.Minutes;
break;
case ClockDisplayAutomation.ToSeconds:
if (DisplayMode == ClockDisplayMode.Hours)
DisplayMode = ClockDisplayMode.Minutes;
else if (DisplayMode == ClockDisplayMode.Minutes)
DisplayMode = ClockDisplayMode.Seconds;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void ClockItemDragStartedHandler(object sender, DragStartedEventArgs dragStartedEventArgs)
{
_currentStartPosition = new Point(dragStartedEventArgs.HorizontalOffset, dragStartedEventArgs.VerticalOffset);
}
private void ClockItemDragDeltaHandler(object sender, DragDeltaEventArgs dragDeltaEventArgs)
{
var currentDragPosition = new Point(_currentStartPosition.X + dragDeltaEventArgs.HorizontalChange, _currentStartPosition.Y + dragDeltaEventArgs.VerticalChange);
var delta = new Point(currentDragPosition.X - _centreCanvas.X, currentDragPosition.Y - _centreCanvas.Y);
var angle = Math.Atan2(delta.X, -delta.Y);
if (angle < 0) angle += 2 * Math.PI;
DateTime time;
if (DisplayMode == ClockDisplayMode.Hours)
{
if (Is24Hours)
{
var outerBoundary = (_centreCanvas.X * ButtonRadiusInnerRatio +
(_centreCanvas.X * ButtonRadiusRatio - _centreCanvas.X * ButtonRadiusInnerRatio) / 2);
var sqrt = Math.Sqrt((_centreCanvas.X - currentDragPosition.X) * (_centreCanvas.X - currentDragPosition.X) + (_centreCanvas.Y - currentDragPosition.Y) * (_centreCanvas.Y - currentDragPosition.Y));
var localIsPostMerdiem = sqrt > outerBoundary;
var hour = (int)Math.Round(6 * angle / Math.PI, MidpointRounding.AwayFromZero) % 12 + (localIsPostMerdiem ? 12 : 0);
if (hour == 12)
hour = 0;
else if (hour == 0)
hour = 12;
time = new DateTime(Time.Year, Time.Month, Time.Day, hour, Time.Minute, Time.Second);
}
else
time = new DateTime(Time.Year, Time.Month, Time.Day,
(int)Math.Round(6 * angle / Math.PI, MidpointRounding.AwayFromZero) % 12 + (IsPostMeridiem ? 12 : 0),
Time.Minute, Time.Second);
}
else
{
var value = (int)Math.Round(30 * angle / Math.PI, MidpointRounding.AwayFromZero) % 60;
if (DisplayMode == ClockDisplayMode.Minutes)
time = new DateTime(Time.Year, Time.Month, Time.Day, Time.Hour, value, Time.Second);
else
time = new DateTime(Time.Year, Time.Month, Time.Day, Time.Hour, Time.Minute, value);
}
SetCurrentValue(TimeProperty, time);
}
private static void SetFlags(Clock clock)
{
clock.IsPostMeridiem = clock.Time.Hour >= 12;
clock.IsMidnightHour = clock.Time.Hour == 0;
clock.IsMiddayHour = clock.Time.Hour == 12;
}
private BindingBase GetBinding(string propertyName, object owner = null, IValueConverter converter = null, object converterParameter = null)
{
var result = new Binding(propertyName) { Source = owner ?? this, Converter = converter, ConverterParameter = converterParameter };
return result;
}
}
}
@@ -0,0 +1,31 @@
using System.Windows;
namespace MaterialDesignThemes.Wpf
{
public delegate void ClockChoiceMadeEventHandler(object sender, ClockChoiceMadeEventArgs e);
public class ClockChoiceMadeEventArgs : RoutedEventArgs
{
private readonly ClockDisplayMode _displayMode;
public ClockChoiceMadeEventArgs(ClockDisplayMode displayMode)
{
_displayMode = displayMode;
}
public ClockChoiceMadeEventArgs(ClockDisplayMode displayMode, RoutedEvent routedEvent) : base(routedEvent)
{
_displayMode = displayMode;
}
public ClockChoiceMadeEventArgs(ClockDisplayMode displayMode, RoutedEvent routedEvent, object source) : base(routedEvent, source)
{
_displayMode = displayMode;
}
public ClockDisplayMode Mode
{
get { return _displayMode; }
}
}
}
@@ -0,0 +1,188 @@
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace MaterialDesignThemes.Wpf
{
[TemplatePart(Name = ThumbPartName, Type = typeof(Thumb))]
public class ClockItemButton : ToggleButton
{
public const string ThumbPartName = "PART_Thumb";
public static readonly DependencyProperty CentreXProperty = DependencyProperty.Register(
nameof(CentreX), typeof (double), typeof (ClockItemButton), new PropertyMetadata(default(double)));
public double CentreX
{
get { return (double) GetValue(CentreXProperty); }
set { SetValue(CentreXProperty, value); }
}
public static readonly DependencyProperty CentreYProperty = DependencyProperty.Register(
nameof(CentreY), typeof (double), typeof (ClockItemButton), new PropertyMetadata(default(double)));
public double CentreY
{
get { return (double) GetValue(CentreYProperty); }
set { SetValue(CentreYProperty, value); }
}
private static readonly DependencyPropertyKey XPropertyKey =
DependencyProperty.RegisterReadOnly(
"X", typeof(double), typeof(ClockItemButton),
new PropertyMetadata(default(double)));
public static readonly DependencyProperty XProperty =
XPropertyKey.DependencyProperty;
public double X
{
get { return (double)GetValue(XProperty); }
private set { SetValue(XPropertyKey, value); }
}
private static readonly DependencyPropertyKey YPropertyKey =
DependencyProperty.RegisterReadOnly(
"Y", typeof(double), typeof(ClockItemButton),
new PropertyMetadata(default(double)));
public static readonly DependencyProperty YProperty =
YPropertyKey.DependencyProperty;
private Thumb _thumb;
public double Y
{
get { return (double)GetValue(YProperty); }
private set { SetValue(YPropertyKey, value); }
}
public override void OnApplyTemplate()
{
if (_thumb != null)
{
_thumb.DragStarted -= ThumbOnDragStarted;
_thumb.DragDelta -= ThumbOnDragDelta;
_thumb.DragCompleted -= ThumbOnDragCompleted;
}
_thumb = GetTemplateChild(ThumbPartName) as Thumb;
if (_thumb != null)
{
_thumb.DragStarted += ThumbOnDragStarted;
_thumb.DragDelta += ThumbOnDragDelta;
_thumb.DragCompleted += ThumbOnDragCompleted;
}
base.OnApplyTemplate();
}
public static readonly RoutedEvent DragDeltaEvent =
EventManager.RegisterRoutedEvent(
"DragDelta",
RoutingStrategy.Bubble,
typeof (DragDeltaEventHandler),
typeof (ClockItemButton));
private static void OnDragDelta(
DependencyObject d, double horizontalChange, double verticalChange)
{
var instance = (ClockItemButton) d;
var dragDeltaEventArgs = new DragDeltaEventArgs(horizontalChange, verticalChange)
{
RoutedEvent = DragDeltaEvent,
Source = d
};
instance.RaiseEvent(dragDeltaEventArgs);
}
public static readonly RoutedEvent DragStartedEvent =
EventManager.RegisterRoutedEvent(
"DragStarted",
RoutingStrategy.Bubble,
typeof(DragStartedEventHandler),
typeof(ClockItemButton));
public static readonly RoutedEvent DragCompletedEvent =
EventManager.RegisterRoutedEvent(
"DragCompleted",
RoutingStrategy.Bubble,
typeof (DragCompletedEventHandler),
typeof (ClockItemButton));
private static void OnDragStarted(DependencyObject d, double horizontalOffset, double verticalOffset)
{
var instance = (ClockItemButton)d;
var dragStartedEventArgs = new DragStartedEventArgs(horizontalOffset, verticalOffset)
{
RoutedEvent = DragStartedEvent,
Source = d
};
instance.RaiseEvent(dragStartedEventArgs);
}
private static void OnDragCompleted(DependencyObject d, double horizontalChange, double verticalChange, bool canceled)
{
var instance = (ClockItemButton)d;
var dragCompletedEventArgs = new DragCompletedEventArgs(horizontalChange, verticalChange, canceled)
{
RoutedEvent = DragCompletedEvent,
Source = d
};
instance.RaiseEvent(dragCompletedEventArgs);
}
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (_thumb == null) return;
base.OnPreviewMouseLeftButtonDown(e);
if (!IsChecked.HasValue || !IsChecked.Value)
{
OnToggle();
}
}
/// <summary>
/// This override method is called when the control is clicked by mouse or keyboard
/// </summary>
protected override void OnClick()
{
if (_thumb == null)
base.OnClick();
}
private void ThumbOnDragStarted(object sender, DragStartedEventArgs dragStartedEventArgs)
{
//Get the absolute position of where the drag operation started
OnDragStarted(this, CentreX + dragStartedEventArgs.HorizontalOffset - Width / 2.0, CentreY + dragStartedEventArgs.VerticalOffset - Height / 2.0);
}
private void ThumbOnDragDelta(object sender, DragDeltaEventArgs dragDeltaEventArgs)
{
OnDragDelta(this, dragDeltaEventArgs.HorizontalChange, dragDeltaEventArgs.VerticalChange);
}
private void ThumbOnDragCompleted(object sender, DragCompletedEventArgs dragCompletedEventArgs)
{
OnDragCompleted(this, dragCompletedEventArgs.HorizontalChange, dragCompletedEventArgs.VerticalChange, dragCompletedEventArgs.Canceled);
}
protected override Size ArrangeOverride(Size finalSize)
{
Dispatcher.BeginInvoke(new Action(() =>
{
X = CentreX - finalSize.Width/2;
Y = CentreY - finalSize.Height/2;
}));
return base.ArrangeOverride(finalSize);
}
}
}
@@ -0,0 +1,46 @@
using System.Windows;
using System.Windows.Controls;
namespace MaterialDesignThemes.Wpf
{
public enum ColorZoneMode
{
Standard,
Inverted,
PrimaryLight,
PrimaryMid,
PrimaryDark,
Accent,
Light,
Dark
}
/// <summary>
/// User a colour zone to easily switch the background and foreground colours, whilst still remaining within the selected Material Design palette.
/// </summary>
public class ColorZone : ContentControl
{
static ColorZone()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorZone), new FrameworkPropertyMetadata(typeof(ColorZone)));
}
public static readonly DependencyProperty ModeProperty = DependencyProperty.Register(
nameof(Mode), typeof (ColorZoneMode), typeof (ColorZone), new PropertyMetadata(default(ColorZoneMode)));
public ColorZoneMode Mode
{
get { return (ColorZoneMode) GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
nameof(CornerRadius), typeof (CornerRadius), typeof (ColorZone), new PropertyMetadata(default(CornerRadius)));
public CornerRadius CornerRadius
{
get { return (CornerRadius) GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
}
}
@@ -0,0 +1,20 @@
using System.Windows;
namespace MaterialDesignThemes.Wpf
{
public static class ColorZoneAssist
{
public static readonly DependencyProperty ModeProperty = DependencyProperty.RegisterAttached(
"Mode", typeof(ColorZoneMode), typeof(ColorZoneAssist), new FrameworkPropertyMetadata(default(ColorZoneMode), FrameworkPropertyMetadataOptions.Inherits));
public static void SetMode(DependencyObject element, ColorZoneMode value)
{
element.SetValue(ModeProperty, value);
}
public static ColorZoneMode GetMode(DependencyObject element)
{
return (ColorZoneMode)element.GetValue(ModeProperty);
}
}
}
@@ -0,0 +1,49 @@
using System.Windows;
namespace MaterialDesignThemes.Wpf
{
public static class ComboBoxAssist
{
/// <summary>
/// By default ComboBox uses the wrapper popup. Popup can be switched to classic Windows desktop view by means of this attached property.
/// </summary>
public static readonly DependencyProperty ClassicModeProperty = DependencyProperty.RegisterAttached(
"ClassicMode",
typeof (bool),
typeof (ComboBoxAssist),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));
public static bool GetClassicMode(DependencyObject element)
{
return (bool)element.GetValue(ClassicModeProperty);
}
public static void SetClassicMode(DependencyObject element, object value)
{
element.SetValue(ClassicModeProperty, value);
}
/// <summary>
/// By default the selected item is hidden from the drop down list, as per Material Design specifications.
/// To revert to a more classic Windows desktop behaviour, and show the currently selected item again in the drop
/// down, set this attached propety to true.
/// </summary>
public static readonly DependencyProperty ShowSelectedItemProperty = DependencyProperty.RegisterAttached(
"ShowSelectedItem",
typeof (bool),
typeof (ComboBoxAssist),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));
public static bool GetShowSelectedItem(DependencyObject element)
{
return (bool)element.GetValue(ShowSelectedItemProperty);
}
public static void SetShowSelectedItem(DependencyObject element, object value)
{
element.SetValue(ShowSelectedItemProperty, value);
}
}
}
@@ -0,0 +1,394 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace MaterialDesignThemes.Wpf
{
public enum ComboBoxPopupPlacement
{
Undefined,
Down,
Up,
Classic
}
public class ComboBoxPopup : Popup
{
#region UpContentTemplate property
public static readonly DependencyProperty UpContentTemplateProperty
= DependencyProperty.Register(nameof(UpContentTemplate),
typeof(ControlTemplate),
typeof(ComboBoxPopup),
new UIPropertyMetadata(null, CreateTemplatePropertyChangedCallback(ComboBoxPopupPlacement.Classic)));
public ControlTemplate UpContentTemplate
{
get { return (ControlTemplate)GetValue(UpContentTemplateProperty); }
set { SetValue(UpContentTemplateProperty, value); }
}
#endregion
#region DownContentTemplate region
public static readonly DependencyProperty DownContentTemplateProperty
= DependencyProperty.Register(nameof(DownContentTemplate),
typeof(ControlTemplate),
typeof(ComboBoxPopup),
new UIPropertyMetadata(null, CreateTemplatePropertyChangedCallback(ComboBoxPopupPlacement.Down)));
public ControlTemplate DownContentTemplate
{
get { return (ControlTemplate)GetValue(DownContentTemplateProperty); }
set { SetValue(DownContentTemplateProperty, value); }
}
#endregion
#region ClassicContentTemplate property
public static readonly DependencyProperty ClassicContentTemplateProperty
= DependencyProperty.Register(nameof(ClassicContentTemplate),
typeof(ControlTemplate),
typeof(ComboBoxPopup),
new UIPropertyMetadata(null, CreateTemplatePropertyChangedCallback(ComboBoxPopupPlacement.Up)));
public ControlTemplate ClassicContentTemplate
{
get { return (ControlTemplate)GetValue(ClassicContentTemplateProperty); }
set { SetValue(ClassicContentTemplateProperty, value); }
}
#endregion
#region UpVerticalOffset property
public static readonly DependencyProperty UpVerticalOffsetProperty
= DependencyProperty.Register(nameof(UpVerticalOffset),
typeof(double),
typeof(ComboBoxPopup),
new PropertyMetadata(0.0));
public double UpVerticalOffset
{
get { return (double)GetValue(UpVerticalOffsetProperty); }
set { SetValue(UpVerticalOffsetProperty, value); }
}
#endregion
#region DownVerticalOffset property
public static readonly DependencyProperty DownVerticalOffsetProperty
= DependencyProperty.Register(nameof(DownVerticalOffset),
typeof(double),
typeof(ComboBoxPopup),
new PropertyMetadata(0.0));
public double DownVerticalOffset
{
get { return (double)GetValue(DownVerticalOffsetProperty); }
set { SetValue(DownVerticalOffsetProperty, value); }
}
#endregion
#region PopupPlacement property
public static readonly DependencyProperty PopupPlacementProperty
= DependencyProperty.Register(nameof(PopupPlacement),
typeof(ComboBoxPopupPlacement),
typeof(ComboBoxPopup),
new PropertyMetadata(ComboBoxPopupPlacement.Undefined, PopupPlacementPropertyChangedCallback));
public ComboBoxPopupPlacement PopupPlacement
{
get { return (ComboBoxPopupPlacement)GetValue(PopupPlacementProperty); }
set { SetValue(PopupPlacementProperty, value); }
}
#endregion
#region Background property
private static readonly DependencyPropertyKey BackgroundPropertyKey =
DependencyProperty.RegisterReadOnly(
"Background", typeof(Brush), typeof(ComboBoxPopup),
new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty BackgroundProperty =
BackgroundPropertyKey.DependencyProperty;
public Brush Background
{
get { return (Brush) GetValue(BackgroundProperty); }
private set { SetValue(BackgroundPropertyKey, value); }
}
#endregion
#region DefaultVerticalOffset
public static readonly DependencyProperty DefaultVerticalOffsetProperty
= DependencyProperty.Register(nameof(DefaultVerticalOffset),
typeof(double),
typeof(ComboBoxPopup),
new PropertyMetadata(0.0));
public double DefaultVerticalOffset
{
get { return (double)GetValue(DefaultVerticalOffsetProperty); }
set { SetValue(DefaultVerticalOffsetProperty, value); }
}
#endregion
#region VisiblePlacementWidth
public double VisiblePlacementWidth
{
get { return (double)GetValue(VisiblePlacementWidthProperty); }
set { SetValue(VisiblePlacementWidthProperty, value); }
}
public static readonly DependencyProperty VisiblePlacementWidthProperty
= DependencyProperty.Register(nameof(VisiblePlacementWidth),
typeof(double),
typeof(ComboBoxPopup),
new PropertyMetadata(0.0));
#endregion
#region ClassicMode property
public static readonly DependencyProperty ClassicModeProperty
= DependencyProperty.Register(
nameof(ClassicMode),
typeof(bool),
typeof(ComboBoxPopup),
new FrameworkPropertyMetadata(true));
public bool ClassicMode
{
get { return (bool)GetValue(ClassicModeProperty); }
set { SetValue(ClassicModeProperty, value); }
}
#endregion
public ComboBoxPopup()
{
CustomPopupPlacementCallback = ComboBoxCustomPopupPlacementCallback;
var childPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(ComboBoxPopup.ChildProperty, typeof(ComboBoxPopup));
EventHandler childChangedHandler = (sender, x) =>
{
if (PopupPlacement != ComboBoxPopupPlacement.Undefined)
{
UpdateChildTemplate(PopupPlacement);
}
};
Loaded += (sender, args) =>
{
childPropertyDescriptor.AddValueChanged(this, childChangedHandler);
};
Unloaded += (sender, args) =>
{
childPropertyDescriptor.RemoveValueChanged(this, childChangedHandler);
};
}
private void SetupBackground(IEnumerable<DependencyObject> visualAncestry)
{
var background = visualAncestry
.Select(v => (v as Control)?.Background ?? (v as Panel)?.Background ?? (v as Border)?.Background)
.FirstOrDefault(v => v != null && !Equals(v, Brushes.Transparent) && v is SolidColorBrush);
if (background != null)
{
Background = background;
}
}
private void SetupVisiblePlacementWidth(IEnumerable<DependencyObject> visualAncestry)
{
var parent = visualAncestry.OfType<Panel>().ElementAt(1);
VisiblePlacementWidth = TreeHelper.GetVisibleWidth((FrameworkElement)PlacementTarget, parent);
}
private CustomPopupPlacement[] ComboBoxCustomPopupPlacementCallback(
Size popupSize, Size targetSize, Point offset)
{
var visualAncestry = PlacementTarget.GetVisualAncestry().ToList();
SetupBackground(visualAncestry);
SetupVisiblePlacementWidth(visualAncestry);
var data = GetPositioningData(visualAncestry, popupSize, targetSize, offset);
var preferUpIfSafe = data.LocationY + data.PopupSize.Height > data.ScreenHeight;
if (ClassicMode
|| data.LocationX + data.PopupSize.Width - data.RealOffsetX > data.ScreenWidth
|| data.LocationX - data.RealOffsetX < 0
|| !preferUpIfSafe && data.LocationY - Math.Abs(data.NewDownY) < 0)
{
SetCurrentValue(PopupPlacementProperty, ComboBoxPopupPlacement.Classic);
return new[] { GetClassicPopupPlacement(this, data) };
}
if (preferUpIfSafe)
{
SetCurrentValue(PopupPlacementProperty, ComboBoxPopupPlacement.Up);
return new[] { GetUpPopupPlacement(data) };
}
SetCurrentValue(PopupPlacementProperty, ComboBoxPopupPlacement.Down);
return new[] { GetDownPopupPlacement(data) };
}
private void SetChildTemplateIfNeed(ControlTemplate template)
{
var contentControl = Child as ContentControl;
if (contentControl == null) return;
//throw new InvalidOperationException($"The type of {nameof(Child)} must be {nameof(ContentControl)}");
if (!ReferenceEquals(contentControl.Template, template))
{
contentControl.Template = template;
}
}
private PositioningData GetPositioningData(IEnumerable<DependencyObject> visualAncestry, Size popupSize, Size targetSize, Point offset)
{
var locationFromScreen = PlacementTarget.PointToScreen(new Point(0, 0));
var mainVisual = visualAncestry.OfType<Visual>().LastOrDefault();
if (mainVisual == null) throw new ArgumentException($"{nameof(visualAncestry)} must contains unless one {nameof(Visual)} control inside.");
var screen = Screen.FromPoint(locationFromScreen);
var screenWidth = (int)DpiHelper.TransformToDeviceX(mainVisual, (int)screen.Bounds.Width);
var screenHeight = (int)DpiHelper.TransformToDeviceY(mainVisual, (int)screen.Bounds.Height);
//Adjust the location to be in terms of the current screen
var locationX = (int)(locationFromScreen.X - screen.Bounds.X) % screenWidth;
var locationY = (int)(locationFromScreen.Y - screen.Bounds.Y) % screenHeight;
var upVerticalOffsetIndepent = DpiHelper.TransformToDeviceY(mainVisual, UpVerticalOffset);
var newUpY = upVerticalOffsetIndepent - popupSize.Height + targetSize.Height;
var newDownY = DpiHelper.TransformToDeviceY(mainVisual, DownVerticalOffset);
double offsetX;
const int rtlHorizontalOffset = 20;
if (FlowDirection == FlowDirection.LeftToRight)
offsetX = DpiHelper.TransformToDeviceX(mainVisual, offset.X);
else
offsetX = DpiHelper.TransformToDeviceX(mainVisual,
offset.X - targetSize.Width - rtlHorizontalOffset);
return new PositioningData(
mainVisual, offsetX,
newUpY, newDownY,
popupSize, targetSize,
locationX, locationY,
screenHeight, screenWidth);
}
private static PropertyChangedCallback CreateTemplatePropertyChangedCallback(ComboBoxPopupPlacement popupPlacement)
{
return delegate (DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var popup = d as ComboBoxPopup;
if (popup == null) return;
var template = e.NewValue as ControlTemplate;
if (template == null) return;
if (popup.PopupPlacement == popupPlacement)
{
popup.SetChildTemplateIfNeed(template);
}
};
}
private void UpdateChildTemplate(ComboBoxPopupPlacement placement)
{
switch (placement)
{
case ComboBoxPopupPlacement.Classic:
SetChildTemplateIfNeed(ClassicContentTemplate);
break;
case ComboBoxPopupPlacement.Down:
SetChildTemplateIfNeed(DownContentTemplate);
break;
case ComboBoxPopupPlacement.Up:
SetChildTemplateIfNeed(UpContentTemplate);
break;
// default:
// throw new NotImplementedException($"Unexpected value {placement} of the {nameof(PopupPlacement)} property inside the {nameof(ComboBoxPopup)} control.");
}
}
private static void PopupPlacementPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var popup = d as ComboBoxPopup;
if (popup == null) return;
if (!(e.NewValue is ComboBoxPopupPlacement)) return;
var placement = (ComboBoxPopupPlacement)e.NewValue;
popup.UpdateChildTemplate(placement);
}
private static CustomPopupPlacement GetClassicPopupPlacement(ComboBoxPopup popup, PositioningData data)
{
var defaultVerticalOffsetIndepent = DpiHelper.TransformToDeviceY(data.MainVisual, popup.DefaultVerticalOffset);
var newY = data.LocationY + data.PopupSize.Height > data.ScreenHeight
? -(defaultVerticalOffsetIndepent + data.PopupSize.Height)
: defaultVerticalOffsetIndepent + data.TargetSize.Height;
return new CustomPopupPlacement(new Point(data.OffsetX, newY), PopupPrimaryAxis.Horizontal);
}
private static CustomPopupPlacement GetDownPopupPlacement(PositioningData data)
{
return new CustomPopupPlacement(new Point(data.OffsetX, data.NewDownY), PopupPrimaryAxis.None);
}
private static CustomPopupPlacement GetUpPopupPlacement(PositioningData data)
{
return new CustomPopupPlacement(new Point(data.OffsetX, data.NewUpY), PopupPrimaryAxis.None);
}
private struct PositioningData
{
public Visual MainVisual { get; }
public double OffsetX { get; }
public double NewUpY { get; }
public double NewDownY { get; }
public double RealOffsetX => (PopupSize.Width - TargetSize.Width) / 2.0;
public Size PopupSize { get; }
public Size TargetSize { get; }
public double LocationX { get; }
public double LocationY { get; }
public double ScreenHeight { get; }
public double ScreenWidth { get; }
public PositioningData(Visual mainVisual, double offsetX, double newUpY, double newDownY, Size popupSize, Size targetSize, double locationX, double locationY, double screenHeight, double screenWidth)
{
MainVisual = mainVisual;
OffsetX = offsetX;
NewUpY = newUpY;
NewDownY = newDownY;
PopupSize = popupSize; TargetSize = targetSize;
LocationX = locationX; LocationY = locationY;
ScreenWidth = screenWidth; ScreenHeight = screenHeight;
}
}
}
}
@@ -0,0 +1,29 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class BooleanToVisibilityConverter : IValueConverter
{
public Visibility TrueValue { get; set; } = Visibility.Visible;
public Visibility FalseValue { get; set; } = Visibility.Collapsed;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool bValue = false;
if (value is bool)
{
bValue = (bool)value;
}
return (bValue) ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value as Visibility? == TrueValue;
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace MaterialDesignThemes.Wpf.Converters
{
public class BrushRoundConverter : IValueConverter
{
public Brush HighValue { get; set; } = Brushes.White;
public Brush LowValue { get; set; } = Brushes.Black;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var solidColorBrush = value as SolidColorBrush;
if (solidColorBrush == null) return null;
var color = solidColorBrush.Color;
var brightness = 0.3 * color.R + 0.59 * color.G + 0.11 * color.B;
return brightness < 123 ? LowValue : HighValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace MaterialDesignThemes.Wpf.Converters
{
public class BrushToRadialGradientBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var solidColorBrush = value as SolidColorBrush;
if (solidColorBrush == null) return Binding.DoNothing;
return new RadialGradientBrush(solidColorBrush.Color, Colors.Transparent)
{
Center = new Point(.5, .5),
GradientOrigin = new Point(.5, .5),
RadiusX = .75,
RadiusY = .75,
Opacity = .39
};
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,33 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
/// <summary>
/// Help us format the content of a header button in a calendar.
/// </summary>
/// <remarks>
/// Expected items, in the following order:
/// 1) DateTime Calendar.DisplayDate
/// 2) DateTime? Calendar.SelectedDate
/// </remarks>
public class CalendarDateCoalesceConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 2) throw new ArgumentException("Unexpected", "values");
if (!(values[0] is DateTime)) throw new ArgumentException("Unexpected", "values");
if (values[1] != null && !(values[1] is DateTime?)) throw new ArgumentException("Unexpected", "values");
var selectedDate = (DateTime?) values[1];
return selectedDate ?? values[0];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}
@@ -0,0 +1,47 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters.CircularProgressBar
{
public class ArcEndPointConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var actualWidth = values[0].ExtractDouble();
var value = values[1].ExtractDouble();
var minimum = values[2].ExtractDouble();
var maximum = values[3].ExtractDouble();
if (new[] {actualWidth, value, minimum, maximum}.AnyNan())
return Binding.DoNothing;
if (values.Length == 5)
{
var fullIndeterminateScaling = values[4].ExtractDouble();
if (!double.IsNaN(fullIndeterminateScaling) && fullIndeterminateScaling > 0.0)
{
value = (maximum - minimum)*fullIndeterminateScaling;
}
}
var percent = maximum <= minimum ? 1.0 : (value - minimum)/(maximum - minimum);
var degrees = 360*percent;
var radians = degrees*(Math.PI/180);
var centre = new Point(actualWidth/2, actualWidth/2);
var hypotenuseRadius = (actualWidth/2);
var adjacent = Math.Cos(radians)*hypotenuseRadius;
var opposite = Math.Sin(radians)*hypotenuseRadius;
return new Point(centre.X + opposite, centre.Y - adjacent);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,25 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters.CircularProgressBar
{
public class ArcSizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double && ((double)value > 0.0))
{
return new Size((double)value / 2, (double)value / 2);
}
return new Point();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,37 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters.CircularProgressBar
{
public class LargeArcConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var value = values[0].ExtractDouble();
var minimum = values[1].ExtractDouble();
var maximum = values[2].ExtractDouble();
if (new[] { value, minimum, maximum }.AnyNan())
return Binding.DoNothing;
if (values.Length == 4)
{
var fullIndeterminateScaling = values[3].ExtractDouble();
if (!double.IsNaN(fullIndeterminateScaling) && fullIndeterminateScaling > 0.0)
{
value = (maximum - minimum) * fullIndeterminateScaling;
}
}
var percent = maximum <= minimum ? 1.0 : (value - minimum) / (maximum - minimum);
return percent > 0.5;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Linq;
namespace MaterialDesignThemes.Wpf.Converters.CircularProgressBar
{
internal static class LocalEx
{
public static double ExtractDouble(this object val)
{
var d = val as double? ?? double.NaN;
return double.IsInfinity(d) ? double.NaN : d;
}
public static bool AnyNan(this IEnumerable<double> vals)
{
return vals.Any(double.IsNaN);
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters.CircularProgressBar
{
public class RotateTransformCentreConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//value == actual width
return (double) value/2;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,28 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters.CircularProgressBar
{
public class RotateTransformConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var value = values[0].ExtractDouble();
var minimum = values[1].ExtractDouble();
var maximum = values[2].ExtractDouble();
if (new[] { value, minimum, maximum }.AnyNan())
return Binding.DoNothing;
var percent = maximum <= minimum ? 1.0 : (value - minimum) / (maximum - minimum);
return 360*percent;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,28 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters.CircularProgressBar
{
public class StartPointConverter : IValueConverter
{
[Obsolete]
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double && ((double) value > 0.0))
{
return new Point((double)value / 2, 0);
}
return new Point();
}
[Obsolete]
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,84 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
internal class ClockItemIsCheckedConverter : IValueConverter
{
private readonly Func<DateTime> _currentTimeGetter;
private readonly ClockDisplayMode _displayMode;
private readonly bool _is24Hours;
public ClockItemIsCheckedConverter(Func<DateTime> currentTimeGetter, ClockDisplayMode displayMode, bool is24Hours)
{
if (currentTimeGetter == null) throw new ArgumentNullException(nameof(currentTimeGetter));
_currentTimeGetter = currentTimeGetter;
_displayMode = displayMode;
_is24Hours = is24Hours;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var dateTime = (DateTime) value;
var i = (int) parameter;
int converted = 0;
if (_displayMode == ClockDisplayMode.Hours )
converted = MassageHour(dateTime.Hour, _is24Hours);
else if (_displayMode == ClockDisplayMode.Minutes)
converted = MassageMinuteSecond(dateTime.Minute);
else
converted = MassageMinuteSecond(dateTime.Second);
return converted == i;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value != true) return Binding.DoNothing;
var currentTime = _currentTimeGetter();
return new DateTime(
currentTime.Year, currentTime.Month, currentTime.Day,
(_displayMode == ClockDisplayMode.Hours) ? ReverseMassageHour((int)parameter, currentTime, _is24Hours) : currentTime.Hour,
(_displayMode == ClockDisplayMode.Minutes) ? ReverseMassageMinuteSecond((int)parameter) : currentTime.Minute,
(_displayMode == ClockDisplayMode.Seconds) ? ReverseMassageMinuteSecond((int)parameter) : currentTime.Second);
}
private static int MassageHour(int val, bool is24Hours)
{
if (is24Hours)
{
return val == 0 ? 24 : val;
}
if (val == 0) return 12;
if (val > 12) return val - 12;
return val;
}
private static int MassageMinuteSecond(int val)
{
return val == 0 ? 60 : val;
}
private static int ReverseMassageHour(int val, DateTime currentTime, bool is24Hours)
{
if (is24Hours)
{
return val == 24 ? 0 : val;
}
return currentTime.Hour < 12
? (val == 12 ? 0 : val)
: (val == 12 ? 12 : val + 12);
}
private static int ReverseMassageMinuteSecond(int val)
{
return val == 60 ? 0 : val;
}
}
}
@@ -0,0 +1,33 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace MaterialDesignThemes.Wpf.Converters
{
public class ClockLineConverter : MarkupExtension, IValueConverter
{
public ClockDisplayMode DisplayMode { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var time = (DateTime) value;
return DisplayMode == ClockDisplayMode.Hours
? ((time.Hour > 13) ? time.Hour - 12 : time.Hour)*(360/12)
: DisplayMode == ClockDisplayMode.Minutes
? (time.Minute == 0 ? 60 : time.Minute)*(360/60)
: (time.Second == 0 ? 60 : time.Second)*(360/60);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
}
@@ -0,0 +1,36 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class DrawerOffsetConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var d = value as double? ?? 0;
if (double.IsInfinity(d) || double.IsNaN(d)) d = 0;
var dock = (parameter is Dock) ? (Dock)parameter : Dock.Left;
switch (dock)
{
case Dock.Top:
return new Thickness(0, 0 - d, 0, 0);
case Dock.Bottom:
return new Thickness(0, 0, 0, 0 - d);
case Dock.Right:
return new Thickness(0, 0, 0 - d, 0);
case Dock.Left:
default:
return new Thickness(0 - d, 0, 0, 0);
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,22 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class EqualityToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value.Equals(parameter)) return Visibility.Visible;
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,27 @@
using System;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
/// <summary>
/// Converter for <see cref="SmartHint"/> control. Can be extended by <see cref="HintProxyFabric.RegisterBuilder(Func{Control, bool}, Func{Control, IHintProxy})"/> method.
/// </summary>
public class HintProxyFabricConverter : IValueConverter
{
private static readonly Lazy<HintProxyFabricConverter> _instance = new Lazy<HintProxyFabricConverter>();
public static HintProxyFabricConverter Instance => _instance.Value;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return HintProxyFabric.Get(value as Control);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,48 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
/// <summary>
/// Helps coerce the correct item container style for a <see cref="ListView"/>, according to whether the list is displaying in standard mode, or using a <see cref="ListView.View"/>, such as a <see cref="GridView"/>.
/// </summary>
public class ListViewGridViewConverter : IValueConverter
{
/// <summary>
/// Item container style to use when <see cref="ListView.View"/> is <c>null</c>.
/// </summary>
public object DefaultValue { get; set; }
/// <summary>
/// Item container style to use when <see cref="ListView.View"/> is not <c>null</c>, typically when a <see cref="GridView"/> is applied.
/// </summary>
public object ViewValue { get; set; }
/// <summary>
/// Returns the item container <see cref="Style"/> to use for a <see cref="ListView"/>.
/// </summary>
/// <param name="value">Should be a <see cref="ListView"/> or <see cref="ViewBase"/> instance.</param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var listView = value as ListView;
if (listView != null)
{
return listView.View != null ? ViewValue : DefaultValue;
}
return value is ViewBase ? ViewValue : DefaultValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,50 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public enum MathOperation
{
Add,
Subtract,
Multiply,
Divide
}
public sealed class MathConverter : IValueConverter
{
public MathOperation Operation { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
double value1 = System.Convert.ToDouble(value, CultureInfo.InvariantCulture);
double value2 = System.Convert.ToDouble(parameter, CultureInfo.InvariantCulture);
switch (Operation)
{
case MathOperation.Add:
return value1 + value2;
case MathOperation.Divide:
return value1 / value2;
case MathOperation.Multiply:
return value1 * value2;
case MathOperation.Subtract:
return value1 - value2;
default:
return Binding.DoNothing;
}
}
catch (FormatException)
{
return Binding.DoNothing;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,38 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public sealed class MathMultipleConverter : IMultiValueConverter
{
public MathOperation Operation { get; set; }
public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value.Length < 2 || value[0] == null || value[1] == null) return Binding.DoNothing;
if (!double.TryParse(value[0].ToString(), out double value1) || !double.TryParse(value[1].ToString(), out double value2))
return 0;
switch (Operation)
{
default:
// (case MathOperation.Add:)
return value1 + value2;
case MathOperation.Divide:
return value1 / value2;
case MathOperation.Multiply:
return value1 * value2;
case MathOperation.Subtract:
return value1 - value2;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class NotConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(value as bool?) ?? !bool.Parse(value.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(value as bool?) ?? !bool.Parse(value.ToString());
}
}
}
@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class NotZeroConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (double.TryParse((value ?? "").ToString(), out double val))
{
return Math.Abs(val) > 0.0;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class NotZeroToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double val;
double.TryParse((value ?? "").ToString(), out val);
return Math.Abs(val) > 0.0 ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class NullableToVisibilityConverter : IValueConverter
{
public Visibility NullValue { get; set; } = Visibility.Collapsed;
public Visibility NotNullValue { get; set; } = Visibility.Visible;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? NullValue : NotNullValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,21 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class NullableDateTimeToCurrentDateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is DateTime)
return value;
return DateTime.Now.Date;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
}
@@ -0,0 +1,35 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class PointValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values?.Length == 2 && values[0] != null && values[1] != null)
{
double x, y;
if (double.TryParse(values[0].ToString(), out x) &&
double.TryParse(values[1].ToString(), out y))
return new Point(x, y);
}
return new Point();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (value is Point)
{
var point = (Point) value;
return new object[] { point.X, point.Y };
}
return new object[0];
}
}
}
@@ -0,0 +1,33 @@
using System;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class RangeLengthConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length != 4 || values.Any(v => v == null))
return Binding.DoNothing;
if (!double.TryParse(values[0].ToString(), out double min)
|| !double.TryParse(values[1].ToString(), out double max)
|| !double.TryParse(values[2].ToString(), out double value)
|| !double.TryParse(values[3].ToString(), out double containerLength))
return Binding.DoNothing;
var percent = (value - min) / (max - min);
var length = percent * containerLength;
return length > containerLength ? containerLength : length;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class RangePositionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if(values == null || values.Length != 3 || values.Any(v => v == null))
return Binding.DoNothing;
if (!double.TryParse(values[0].ToString(), out double positionAsScaleFactor)
|| !double.TryParse(values[1].ToString(), out double lower)
|| !double.TryParse(values[2].ToString(), out double upper))
return Binding.DoNothing;
var result = upper + (lower - upper)*positionAsScaleFactor;
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,38 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media.Effects;
namespace MaterialDesignThemes.Wpf.Converters
{
public class ShadowConverter : IValueConverter
{
public static readonly ShadowConverter Instance = new ShadowConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is ShadowDepth)) return null;
return Clone(ShadowInfo.GetDropShadow((ShadowDepth) value));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private static DropShadowEffect Clone(DropShadowEffect dropShadowEffect)
{
if (dropShadowEffect == null) return null;
return new DropShadowEffect()
{
BlurRadius = dropShadowEffect.BlurRadius,
Color = dropShadowEffect.Color,
Direction = dropShadowEffect.Direction,
Opacity = dropShadowEffect.Opacity,
RenderingBias = dropShadowEffect.RenderingBias,
ShadowDepth = dropShadowEffect.ShadowDepth
};
}
}
}
@@ -0,0 +1,76 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Effects;
namespace MaterialDesignThemes.Wpf.Converters
{
public class ShadowEdgeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values?.Length != 4)
{
return Binding.DoNothing;
}
if (!(values[0] is double) || !(values[1] is double) || !(values[2] is ShadowDepth) ||
!(values[3] is ShadowEdges))
{
return Binding.DoNothing;
}
double width = (double)values[0];
double height = (double)values[1];
if (double.IsNaN(width) || double.IsInfinity(width) || double.IsNaN(height) || double.IsInfinity(height))
{
return Binding.DoNothing;
}
DropShadowEffect dropShadow = ShadowInfo.GetDropShadow((ShadowDepth)values[2]);
if (dropShadow == null)
{
return Binding.DoNothing;
}
ShadowEdges edges = (ShadowEdges)values[3];
double blurRadius = dropShadow.BlurRadius;
var rect = new Rect(0, 0, width, height);
if (edges.HasFlag(ShadowEdges.Left))
{
rect = new Rect(rect.X - blurRadius, rect.Y, rect.Width + blurRadius, rect.Height);
}
if (edges.HasFlag(ShadowEdges.Top))
{
rect = new Rect(rect.X, rect.Y - blurRadius, rect.Width, rect.Height + blurRadius);
}
if (edges.HasFlag(ShadowEdges.Right))
{
rect = new Rect(rect.X, rect.Y, rect.Width + blurRadius, rect.Height);
}
if (edges.HasFlag(ShadowEdges.Bottom))
{
rect = new Rect(rect.X, rect.Y, rect.Width, rect.Height + blurRadius);
}
var size = new GeometryDrawing(new SolidColorBrush(Colors.White), new Pen(), new RectangleGeometry(rect));
return new DrawingBrush(size)
{
Stretch = Stretch.None,
TileMode = TileMode.None,
Viewport = rect,
ViewportUnits = BrushMappingMode.Absolute,
Viewbox = rect,
ViewboxUnits = BrushMappingMode.Absolute
};
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Effects;
namespace MaterialDesignThemes.Wpf.Converters
{
internal static class ShadowInfo
{
private static readonly IDictionary<ShadowDepth, DropShadowEffect> ShadowsDictionary;
static ShadowInfo()
{
var resourceDictionary = new ResourceDictionary { Source = new Uri("pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Shadows.xaml", UriKind.Absolute) };
ShadowsDictionary = new Dictionary<ShadowDepth, DropShadowEffect>
{
{ ShadowDepth.Depth0, null },
{ ShadowDepth.Depth1, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth1"] },
{ ShadowDepth.Depth2, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth2"] },
{ ShadowDepth.Depth3, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth3"] },
{ ShadowDepth.Depth4, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth4"] },
{ ShadowDepth.Depth5, (DropShadowEffect)resourceDictionary["MaterialDesignShadowDepth5"] },
};
}
public static DropShadowEffect GetDropShadow(ShadowDepth depth)
{
return ShadowsDictionary[depth];
}
}
}
@@ -0,0 +1,41 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class CardClipConverter : IMultiValueConverter
{
/// <summary>
/// 1 - Content presenter render size,
/// 2 - Clipping border padding (main control padding)
/// </summary>
/// <param name="values"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 2 || !(values[0] is Size) || !(values[1] is Thickness))
return Binding.DoNothing;
var size = (Size)values[0];
var farPoint = new Point(
Math.Max(0, size.Width),
Math.Max(0, size.Height));
var padding = (Thickness)values[1];
farPoint.Offset(padding.Left + padding.Right, padding.Top + padding.Bottom);
return new Rect(
new Point(),
new Point(farPoint.X, farPoint.Y));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}
@@ -0,0 +1,28 @@
using System;
using System.ComponentModel;
using System.Globalization;
namespace MaterialDesignThemes.Wpf.Converters
{
public class SnackbarMessageTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var s = value as string;
if (s != null)
{
return new SnackbarMessage
{
Content = s
};
}
return base.ConvertFrom(context, culture, value);
}
}
}
@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MaterialDesignThemes.Wpf.Converters
{
public class TextFieldHintVisibilityConverter : IValueConverter
{
public Visibility IsEmptyValue { get; set; } = Visibility.Visible;
public Visibility IsNotEmptyValue { get; set; } = Visibility.Hidden;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return string.IsNullOrEmpty((value ?? "").ToString()) ? IsEmptyValue : IsNotEmptyValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,30 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace MaterialDesignThemes.Wpf.Converters
{
public class TimeToVisibilityConverter: MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var time = (DateTime)value;
var isPm = ((time.Hour >= 13) || (time.Hour == 0));
return isPm ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
@@ -0,0 +1,16 @@
using System.Windows;
using System.Windows.Controls.Primitives;
namespace MaterialDesignThemes.Wpf
{
public static class CustomPopupPlacementCallbackHelper
{
public static readonly CustomPopupPlacementCallback LargePopupCallback;
static CustomPopupPlacementCallbackHelper()
{
LargePopupCallback =
(size, targetSize, offset) => new[] {new CustomPopupPlacement(new Point(), PopupPrimaryAxis.Horizontal)};
}
}
}
@@ -0,0 +1,226 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
namespace MaterialDesignThemes.Wpf
{
public static class DataGridAssist
{
private static DataGrid _suppressComboAutoDropDown;
public static readonly DependencyProperty AutoGeneratedCheckBoxStyleProperty = DependencyProperty
.RegisterAttached(
"AutoGeneratedCheckBoxStyle", typeof (Style), typeof (DataGridAssist),
new PropertyMetadata(default(Style), AutoGeneratedCheckBoxStylePropertyChangedCallback));
private static void AutoGeneratedCheckBoxStylePropertyChangedCallback(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
((DataGrid) dependencyObject).AutoGeneratingColumn += (sender, args) =>
{
var dataGridCheckBoxColumn = args.Column as DataGridCheckBoxColumn;
if (dataGridCheckBoxColumn == null) return;
dataGridCheckBoxColumn.ElementStyle = GetAutoGeneratedCheckBoxStyle(dependencyObject);
};
}
public static void SetAutoGeneratedCheckBoxStyle(DependencyObject element, Style value)
{
element.SetValue(AutoGeneratedCheckBoxStyleProperty, value);
}
public static Style GetAutoGeneratedCheckBoxStyle(DependencyObject element)
{
return (Style) element.GetValue(AutoGeneratedCheckBoxStyleProperty);
}
public static readonly DependencyProperty AutoGeneratedEditingCheckBoxStyleProperty = DependencyProperty
.RegisterAttached(
"AutoGeneratedEditingCheckBoxStyle", typeof (Style), typeof (DataGridAssist),
new PropertyMetadata(default(Style), AutoGeneratedEditingCheckBoxStylePropertyChangedCallback));
private static void AutoGeneratedEditingCheckBoxStylePropertyChangedCallback(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
((DataGrid) dependencyObject).AutoGeneratingColumn += (sender, args) =>
{
var dataGridCheckBoxColumn = args.Column as DataGridCheckBoxColumn;
if (dataGridCheckBoxColumn == null) return;
dataGridCheckBoxColumn.EditingElementStyle = GetAutoGeneratedEditingCheckBoxStyle(dependencyObject);
};
}
public static void SetAutoGeneratedEditingCheckBoxStyle(DependencyObject element, Style value)
{
element.SetValue(AutoGeneratedEditingCheckBoxStyleProperty, value);
}
public static Style GetAutoGeneratedEditingCheckBoxStyle(DependencyObject element)
{
return (Style) element.GetValue(AutoGeneratedEditingCheckBoxStyleProperty);
}
public static readonly DependencyProperty AutoGeneratedEditingTextStyleProperty = DependencyProperty
.RegisterAttached(
"AutoGeneratedEditingTextStyle", typeof (Style), typeof (DataGridAssist),
new PropertyMetadata(default(Style), AutoGeneratedEditingTextStylePropertyChangedCallback));
private static void AutoGeneratedEditingTextStylePropertyChangedCallback(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
((DataGrid) dependencyObject).AutoGeneratingColumn += (sender, args) =>
{
var dataGridTextColumn = args.Column as DataGridTextColumn;
if (dataGridTextColumn == null) return;
dataGridTextColumn.EditingElementStyle = GetAutoGeneratedEditingTextStyle(dependencyObject);
};
}
public static void SetAutoGeneratedEditingTextStyle(DependencyObject element, Style value)
{
element.SetValue(AutoGeneratedEditingTextStyleProperty, value);
}
public static Style GetAutoGeneratedEditingTextStyle(DependencyObject element)
{
return (Style) element.GetValue(AutoGeneratedEditingTextStyleProperty);
}
public static readonly DependencyProperty CellPaddingProperty = DependencyProperty.RegisterAttached(
"CellPadding", typeof (Thickness), typeof (DataGridAssist),
new FrameworkPropertyMetadata(new Thickness(13, 8, 8, 8), FrameworkPropertyMetadataOptions.Inherits));
public static void SetCellPadding(DependencyObject element, Thickness value)
{
element.SetValue(CellPaddingProperty, value);
}
public static Thickness GetCellPadding(DependencyObject element)
{
return (Thickness) element.GetValue(CellPaddingProperty);
}
public static readonly DependencyProperty ColumnHeaderPaddingProperty = DependencyProperty.RegisterAttached(
"ColumnHeaderPadding", typeof (Thickness), typeof (DataGridAssist),
new FrameworkPropertyMetadata(new Thickness(8), FrameworkPropertyMetadataOptions.Inherits));
public static void SetColumnHeaderPadding(DependencyObject element, Thickness value)
{
element.SetValue(ColumnHeaderPaddingProperty, value);
}
public static Thickness GetColumnHeaderPadding(DependencyObject element)
{
return (Thickness) element.GetValue(ColumnHeaderPaddingProperty);
}
public static readonly DependencyProperty EnableEditBoxAssistProperty = DependencyProperty.RegisterAttached(
"EnableEditBoxAssist", typeof (bool), typeof (DataGridAssist),
new PropertyMetadata(default(bool), EnableCheckBoxAssistPropertyChangedCallback));
public static void SetEnableEditBoxAssist(DependencyObject element, bool value)
{
element.SetValue(EnableEditBoxAssistProperty, value);
}
public static bool GetEnableEditBoxAssist(DependencyObject element)
{
return (bool) element.GetValue(EnableEditBoxAssistProperty);
}
private static void EnableCheckBoxAssistPropertyChangedCallback(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var dataGrid = dependencyObject as DataGrid;
if (dataGrid == null) return;
if ((bool) dependencyPropertyChangedEventArgs.NewValue)
dataGrid.PreviewMouseLeftButtonDown += DataGridOnPreviewMouseLeftButtonDown;
else
dataGrid.PreviewMouseLeftButtonDown -= DataGridOnPreviewMouseLeftButtonDown;
}
private static void DataGridOnPreviewMouseLeftButtonDown(object sender,
MouseButtonEventArgs mouseButtonEventArgs)
{
var dataGrid = (DataGrid) sender;
var inputHitTest =
dataGrid.InputHitTest(mouseButtonEventArgs.GetPosition((DataGrid) sender)) as DependencyObject;
while (inputHitTest != null)
{
var dataGridCell = inputHitTest as DataGridCell;
if (dataGridCell != null && dataGrid.Equals(dataGridCell.GetVisualAncestry().OfType<DataGrid>().FirstOrDefault()))
{
if (dataGridCell.IsReadOnly) return;
ToggleButton toggleButton;
ComboBox comboBox;
if (IsDirectHitOnEditComponent(dataGridCell, mouseButtonEventArgs, out toggleButton))
{
dataGrid.CurrentCell = new DataGridCellInfo(dataGridCell);
dataGrid.BeginEdit();
toggleButton.SetCurrentValue(ToggleButton.IsCheckedProperty, !toggleButton.IsChecked);
dataGrid.CommitEdit();
mouseButtonEventArgs.Handled = true;
}
else if (IsDirectHitOnEditComponent(dataGridCell, mouseButtonEventArgs, out comboBox))
{
if (_suppressComboAutoDropDown != null) return;
dataGrid.CurrentCell = new DataGridCellInfo(dataGridCell);
dataGrid.BeginEdit();
//check again, as we move to the edit template
if (IsDirectHitOnEditComponent(dataGridCell, mouseButtonEventArgs, out comboBox))
{
_suppressComboAutoDropDown = dataGrid;
comboBox.DropDownClosed += ComboBoxOnDropDownClosed;
comboBox.IsDropDownOpen = true;
}
mouseButtonEventArgs.Handled = true;
}
return;
}
inputHitTest = (inputHitTest is Visual || inputHitTest is Visual3D)
? VisualTreeHelper.GetParent(inputHitTest)
: null;
}
}
private static void ComboBoxOnDropDownClosed(object sender, EventArgs eventArgs)
{
_suppressComboAutoDropDown.CommitEdit();
_suppressComboAutoDropDown = null;
((ComboBox)sender).DropDownClosed -= ComboBoxOnDropDownClosed;
}
private static bool IsDirectHitOnEditComponent<TControl>(ContentControl contentControl, MouseEventArgs mouseButtonEventArgs, out TControl control)
where TControl : Control
{
control = contentControl.Content as TControl;
if (control == null) return false;
var frameworkElement = VisualTreeHelper.GetChild(contentControl, 0) as FrameworkElement;
if (frameworkElement == null) return false;
var transformToAncestor = (MatrixTransform) control.TransformToAncestor(frameworkElement);
var rect = new Rect(
new Point(transformToAncestor.Value.OffsetX, transformToAncestor.Value.OffsetY),
new Size(control.ActualWidth, control.ActualHeight));
return rect.Contains(mouseButtonEventArgs.GetPosition(frameworkElement));
}
}
}
@@ -0,0 +1,51 @@
using System;
using System.Globalization;
using System.Linq;
namespace MaterialDesignThemes.Wpf
{
internal static class DateTimeEx
{
internal static DateTimeFormatInfo GetDateFormat(this CultureInfo culture)
{
if (culture == null) throw new ArgumentNullException(nameof(culture));
if (culture.Calendar is GregorianCalendar)
{
return culture.DateTimeFormat;
}
GregorianCalendar foundCal = null;
DateTimeFormatInfo dtfi = null;
foreach (var cal in culture.OptionalCalendars.OfType<GregorianCalendar>())
{
// Return the first Gregorian calendar with CalendarType == Localized
// Otherwise return the first Gregorian calendar
if (foundCal == null)
{
foundCal = cal;
}
if (cal.CalendarType != GregorianCalendarTypes.Localized) continue;
foundCal = cal;
break;
}
if (foundCal == null)
{
// if there are no GregorianCalendars in the OptionalCalendars list, use the invariant dtfi
dtfi = ((CultureInfo)CultureInfo.InvariantCulture.Clone()).DateTimeFormat;
dtfi.Calendar = new GregorianCalendar();
}
else
{
dtfi = ((CultureInfo)culture.Clone()).DateTimeFormat;
dtfi.Calendar = foundCal;
}
return dtfi;
}
}
}
@@ -0,0 +1,61 @@
using System;
using System.Windows;
namespace MaterialDesignThemes.Wpf
{
public class DialogClosingEventArgs : RoutedEventArgs
{
public DialogClosingEventArgs(DialogSession session, object parameter)
{
if (session == null) throw new ArgumentNullException(nameof(session));
Session = session;
Parameter = parameter;
}
public DialogClosingEventArgs(DialogSession session, object parameter, RoutedEvent routedEvent) : base(routedEvent)
{
if (session == null) throw new ArgumentNullException(nameof(session));
Session = session;
Parameter = parameter;
}
public DialogClosingEventArgs(DialogSession session, object parameter, RoutedEvent routedEvent, object source) : base(routedEvent, source)
{
if (session == null) throw new ArgumentNullException(nameof(session));
Session = session;
Parameter = parameter;
}
/// <summary>
/// Cancel the close.
/// </summary>
public void Cancel()
{
IsCancelled = true;
}
/// <summary>
/// Indicates if the close has already been cancelled.
/// </summary>
public bool IsCancelled { get; private set; }
/// <summary>
/// Gets the paramter originally provided to <see cref="DialogHost.CloseDialogCommand"/>/
/// </summary>
public object Parameter { get; }
/// <summary>
/// Allows interation with the current dialog session.
/// </summary>
public DialogSession Session { get; }
/// <summary>
/// Gets the <see cref="DialogHost.DialogContent"/> which is currently displayed, so this could be a view model or a UI element.
/// </summary>
[Obsolete("Prefer Session.Content")]
public object Content => Session.Content;
}
}
@@ -0,0 +1,4 @@
namespace MaterialDesignThemes.Wpf
{
public delegate void DialogClosingEventHandler(object sender, DialogClosingEventArgs eventArgs);
}
@@ -0,0 +1,734 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
using MaterialDesignThemes.Wpf.Transitions;
namespace MaterialDesignThemes.Wpf
{
/// <summary>
/// Defines how a data context is sourced for a dialog if a <see cref="FrameworkElement"/>
/// is passed as the command parameter when using <see cref="DialogHost.OpenDialogCommand"/>.
/// </summary>
public enum DialogHostOpenDialogCommandDataContextSource
{
/// <summary>
/// The data context from the sender element (typically a <see cref="Button"/>)
/// is applied to the content.
/// </summary>
SenderElement,
/// <summary>
/// The data context from the <see cref="DialogHost"/> is applied to the content.
/// </summary>
DialogHostInstance,
/// <summary>
/// The data context is explicitly set to <c>null</c>.
/// </summary>
None
}
[TemplatePart(Name = PopupPartName, Type = typeof(Popup))]
[TemplatePart(Name = PopupPartName, Type = typeof(ContentControl))]
[TemplatePart(Name = ContentCoverGridName, Type = typeof(Grid))]
[TemplateVisualState(GroupName = "PopupStates", Name = OpenStateName)]
[TemplateVisualState(GroupName = "PopupStates", Name = ClosedStateName)]
public class DialogHost : ContentControl
{
public const string PopupPartName = "PART_Popup";
public const string PopupContentPartName = "PART_PopupContentElement";
public const string ContentCoverGridName = "PART_ContentCoverGrid";
public const string OpenStateName = "Open";
public const string ClosedStateName = "Closed";
/// <summary>
/// Routed command to be used somewhere inside an instance to trigger showing of the dialog. Content can be passed to the dialog via a <see cref="Button.CommandParameter"/>.
/// </summary>
public static RoutedCommand OpenDialogCommand = new RoutedCommand();
/// <summary>
/// Routed command to be used inside dialog content to close a dialog. Use a <see cref="Button.CommandParameter"/> to indicate the result of the parameter.
/// </summary>
public static RoutedCommand CloseDialogCommand = new RoutedCommand();
private static readonly HashSet<DialogHost> LoadedInstances = new HashSet<DialogHost>();
private readonly ManualResetEvent _asyncShowWaitHandle = new ManualResetEvent(false);
private DialogOpenedEventHandler _asyncShowOpenedEventHandler;
private DialogClosingEventHandler _asyncShowClosingEventHandler;
private Popup _popup;
private ContentControl _popupContentControl;
private Grid _contentCoverGrid;
private DialogSession _session;
private DialogOpenedEventHandler _attachedDialogOpenedEventHandler;
private DialogClosingEventHandler _attachedDialogClosingEventHandler;
private object _closeDialogExecutionParameter;
private IInputElement _restoreFocusDialogClose;
private IInputElement _restoreFocusWindowReactivation;
private Action _currentSnackbarMessageQueueUnPauseAction = null;
private Action _closeCleanUp = () => { };
static DialogHost()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DialogHost), new FrameworkPropertyMetadata(typeof(DialogHost)));
}
#region .Show overloads
/// <summary>
/// Shows a modal dialog. To use, a <see cref="DialogHost"/> instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML).
/// </summary>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <returns>Task result is the parameter used to close the dialog, typically what is passed to the <see cref="CloseDialogCommand"/> command.</returns>
public static async Task<object> Show(object content)
{
return await Show(content, null, null);
}
/// <summary>
/// Shows a modal dialog. To use, a <see cref="DialogHost"/> instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML).
/// </summary>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="openedEventHandler">Allows access to opened event which would otherwise have been subscribed to on a instance.</param>
/// <returns>Task result is the parameter used to close the dialog, typically what is passed to the <see cref="CloseDialogCommand"/> command.</returns>
public static async Task<object> Show(object content, DialogOpenedEventHandler openedEventHandler)
{
return await Show(content, null, openedEventHandler, null);
}
/// <summary>
/// Shows a modal dialog. To use, a <see cref="DialogHost"/> instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML).
/// </summary>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="closingEventHandler">Allows access to closing event which would otherwise have been subscribed to on a instance.</param>
/// <returns>Task result is the parameter used to close the dialog, typically what is passed to the <see cref="CloseDialogCommand"/> command.</returns>
public static async Task<object> Show(object content, DialogClosingEventHandler closingEventHandler)
{
return await Show(content, null, null, closingEventHandler);
}
/// <summary>
/// Shows a modal dialog. To use, a <see cref="DialogHost"/> instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML).
/// </summary>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="openedEventHandler">Allows access to opened event which would otherwise have been subscribed to on a instance.</param>
/// <param name="closingEventHandler">Allows access to closing event which would otherwise have been subscribed to on a instance.</param>
/// <returns>Task result is the parameter used to close the dialog, typically what is passed to the <see cref="CloseDialogCommand"/> command.</returns>
public static async Task<object> Show(object content, DialogOpenedEventHandler openedEventHandler, DialogClosingEventHandler closingEventHandler)
{
return await Show(content, null, openedEventHandler, closingEventHandler);
}
/// <summary>
/// Shows a modal dialog. To use, a <see cref="DialogHost"/> instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML).
/// </summary>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="dialogIdentifier"><see cref="Identifier"/> of the instance where the dialog should be shown. Typically this will match an identifer set in XAML. <c>null</c> is allowed.</param>
/// <returns>Task result is the parameter used to close the dialog, typically what is passed to the <see cref="CloseDialogCommand"/> command.</returns>
public static async Task<object> Show(object content, object dialogIdentifier)
{
return await Show(content, dialogIdentifier, null, null);
}
/// <summary>
/// Shows a modal dialog. To use, a <see cref="DialogHost"/> instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML).
/// </summary>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="dialogIdentifier"><see cref="Identifier"/> of the instance where the dialog should be shown. Typically this will match an identifer set in XAML. <c>null</c> is allowed.</param>
/// <param name="openedEventHandler">Allows access to opened event which would otherwise have been subscribed to on a instance.</param>
/// <returns>Task result is the parameter used to close the dialog, typically what is passed to the <see cref="CloseDialogCommand"/> command.</returns>
public static Task<object> Show(object content, object dialogIdentifier, DialogOpenedEventHandler openedEventHandler)
{
return Show(content, dialogIdentifier, openedEventHandler, null);
}
/// <summary>
/// Shows a modal dialog. To use, a <see cref="DialogHost"/> instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML).
/// </summary>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="dialogIdentifier"><see cref="Identifier"/> of the instance where the dialog should be shown. Typically this will match an identifer set in XAML. <c>null</c> is allowed.</param>
/// <param name="closingEventHandler">Allows access to closing event which would otherwise have been subscribed to on a instance.</param>
/// <returns>Task result is the parameter used to close the dialog, typically what is passed to the <see cref="CloseDialogCommand"/> command.</returns>
public static Task<object> Show(object content, object dialogIdentifier, DialogClosingEventHandler closingEventHandler)
{
return Show(content, dialogIdentifier, null, closingEventHandler);
}
/// <summary>
/// Shows a modal dialog. To use, a <see cref="DialogHost"/> instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML).
/// </summary>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="dialogIdentifier"><see cref="Identifier"/> of the instance where the dialog should be shown. Typically this will match an identifer set in XAML. <c>null</c> is allowed.</param>
/// <param name="openedEventHandler">Allows access to opened event which would otherwise have been subscribed to on a instance.</param>
/// <param name="closingEventHandler">Allows access to closing event which would otherwise have been subscribed to on a instance.</param>
/// <returns>Task result is the parameter used to close the dialog, typically what is passed to the <see cref="CloseDialogCommand"/> command.</returns>
public static async Task<object> Show(object content, object dialogIdentifier, DialogOpenedEventHandler openedEventHandler, DialogClosingEventHandler closingEventHandler)
{
if (content == null) throw new ArgumentNullException(nameof(content));
if (LoadedInstances.Count == 0)
throw new InvalidOperationException("No loaded DialogHost instances.");
LoadedInstances.First().Dispatcher.VerifyAccess();
var targets = LoadedInstances.Where(dh => dialogIdentifier == null || Equals(dh.Identifier, dialogIdentifier)).ToList();
if (targets.Count == 0)
throw new InvalidOperationException("No loaded DialogHost have an Identifier property matching dialogIndetifier argument.");
if (targets.Count > 1)
throw new InvalidOperationException("Multiple viable DialogHosts. Specify a unique Identifier on each DialogHost, especially where multiple Windows are a concern.");
return await targets[0].ShowInternal(content, openedEventHandler, closingEventHandler);
}
internal async Task<object> ShowInternal(object content, DialogOpenedEventHandler openedEventHandler, DialogClosingEventHandler closingEventHandler)
{
if (IsOpen)
throw new InvalidOperationException("DialogHost is already open.");
AssertTargetableContent();
DialogContent = content;
_asyncShowOpenedEventHandler = openedEventHandler;
_asyncShowClosingEventHandler = closingEventHandler;
SetCurrentValue(IsOpenProperty, true);
var task = new Task(() =>
{
_asyncShowWaitHandle.WaitOne();
});
task.Start();
await task;
_asyncShowOpenedEventHandler = null;
_asyncShowClosingEventHandler = null;
return _closeDialogExecutionParameter;
}
#endregion
public DialogHost()
{
Loaded += OnLoaded;
Unloaded += OnUnloaded;
CommandBindings.Add(new CommandBinding(CloseDialogCommand, CloseDialogHandler, CloseDialogCanExecute));
CommandBindings.Add(new CommandBinding(OpenDialogCommand, OpenDialogHandler));
}
public static readonly DependencyProperty IdentifierProperty = DependencyProperty.Register(
nameof(Identifier), typeof(object), typeof(DialogHost), new PropertyMetadata(default(object)));
/// <summary>
/// Identifier which is used in conjunction with <see cref="Show(object)"/> to determine where a dialog should be shown.
/// </summary>
public object Identifier
{
get { return GetValue(IdentifierProperty); }
set { SetValue(IdentifierProperty, value); }
}
public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register(
nameof(IsOpen), typeof(bool), typeof(DialogHost), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsOpenPropertyChangedCallback));
private static void IsOpenPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var dialogHost = (DialogHost)dependencyObject;
if (dialogHost._popupContentControl != null)
ValidationAssist.SetSuppress(dialogHost._popupContentControl, !dialogHost.IsOpen);
VisualStateManager.GoToState(dialogHost, dialogHost.SelectState(), !TransitionAssist.GetDisableTransitions(dialogHost));
if (dialogHost.IsOpen)
{
WatchWindowActivation(dialogHost);
dialogHost._currentSnackbarMessageQueueUnPauseAction = dialogHost.SnackbarMessageQueue?.Pause();
}
else
{
dialogHost._asyncShowWaitHandle.Set();
dialogHost._attachedDialogClosingEventHandler = null;
if (dialogHost._currentSnackbarMessageQueueUnPauseAction != null)
{
dialogHost._currentSnackbarMessageQueueUnPauseAction();
dialogHost._currentSnackbarMessageQueueUnPauseAction = null;
}
dialogHost._session.IsEnded = true;
dialogHost._session = null;
dialogHost._closeCleanUp();
// Don't attempt to Invoke if _restoreFocusDialogClose hasn't been assigned yet. Can occur
// if the MainWindow has started up minimized. Even when Show() has been called, this doesn't
// seem to have been set.
dialogHost.Dispatcher.InvokeAsync(() => dialogHost._restoreFocusDialogClose?.Focus(), DispatcherPriority.Input);
return;
}
dialogHost._asyncShowWaitHandle.Reset();
dialogHost._session = new DialogSession(dialogHost);
var window = Window.GetWindow(dialogHost);
dialogHost._restoreFocusDialogClose = window != null ? FocusManager.GetFocusedElement(window) : null;
//multiple ways of calling back that the dialog has opened:
// * routed event
// * the attached property (which should be applied to the button which opened the dialog
// * straight forward dependency property
// * handler provided to the async show method
var dialogOpenedEventArgs = new DialogOpenedEventArgs(dialogHost._session, DialogOpenedEvent);
dialogHost.OnDialogOpened(dialogOpenedEventArgs);
dialogHost._attachedDialogOpenedEventHandler?.Invoke(dialogHost, dialogOpenedEventArgs);
dialogHost.DialogOpenedCallback?.Invoke(dialogHost, dialogOpenedEventArgs);
dialogHost._asyncShowOpenedEventHandler?.Invoke(dialogHost, dialogOpenedEventArgs);
dialogHost.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
CommandManager.InvalidateRequerySuggested();
UIElement child = dialogHost.FocusPopup();
if (child != null)
{
//https://github.com/ButchersBoy/MaterialDesignInXamlToolkit/issues/187
//totally not happy about this, but on immediate validation we can get some weird looking stuff...give WPF a kick to refresh...
Task.Delay(300).ContinueWith(t => child.Dispatcher.BeginInvoke(new Action(() => child.InvalidateVisual())));
}
}));
}
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
public static readonly DependencyProperty DialogContentProperty = DependencyProperty.Register(
nameof(DialogContent), typeof(object), typeof(DialogHost), new PropertyMetadata(default(object)));
public object DialogContent
{
get { return (object)GetValue(DialogContentProperty); }
set { SetValue(DialogContentProperty, value); }
}
public static readonly DependencyProperty DialogContentTemplateProperty = DependencyProperty.Register(
nameof(DialogContentTemplate), typeof(DataTemplate), typeof(DialogHost), new PropertyMetadata(default(DataTemplate)));
public DataTemplate DialogContentTemplate
{
get { return (DataTemplate)GetValue(DialogContentTemplateProperty); }
set { SetValue(DialogContentTemplateProperty, value); }
}
public static readonly DependencyProperty DialogContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(DialogContentTemplateSelector), typeof(DataTemplateSelector), typeof(DialogHost), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector DialogContentTemplateSelector
{
get { return (DataTemplateSelector)GetValue(DialogContentTemplateSelectorProperty); }
set { SetValue(DialogContentTemplateSelectorProperty, value); }
}
public static readonly DependencyProperty DialogContentStringFormatProperty = DependencyProperty.Register(
nameof(DialogContentStringFormat), typeof(string), typeof(DialogHost), new PropertyMetadata(default(string)));
public string DialogContentStringFormat
{
get { return (string)GetValue(DialogContentStringFormatProperty); }
set { SetValue(DialogContentStringFormatProperty, value); }
}
public static readonly DependencyProperty DialogMarginProperty = DependencyProperty.Register(
"DialogMargin", typeof(Thickness), typeof(DialogHost), new PropertyMetadata(default(Thickness)));
public Thickness DialogMargin
{
get { return (Thickness)GetValue(DialogMarginProperty); }
set { SetValue(DialogMarginProperty, value); }
}
public static readonly DependencyProperty OpenDialogCommandDataContextSourceProperty = DependencyProperty.Register(
nameof(OpenDialogCommandDataContextSource), typeof(DialogHostOpenDialogCommandDataContextSource), typeof(DialogHost), new PropertyMetadata(default(DialogHostOpenDialogCommandDataContextSource)));
/// <summary>
/// Defines how a data context is sourced for a dialog if a <see cref="FrameworkElement"/>
/// is passed as the command parameter when using <see cref="DialogHost.OpenDialogCommand"/>.
/// </summary>
public DialogHostOpenDialogCommandDataContextSource OpenDialogCommandDataContextSource
{
get { return (DialogHostOpenDialogCommandDataContextSource)GetValue(OpenDialogCommandDataContextSourceProperty); }
set { SetValue(OpenDialogCommandDataContextSourceProperty, value); }
}
public static readonly DependencyProperty CloseOnClickAwayProperty = DependencyProperty.Register(
"CloseOnClickAway", typeof(bool), typeof(DialogHost), new PropertyMetadata(default(bool)));
/// <summary>
/// Indicates whether the dialog will close if the user clicks off the dialog, on the obscured background.
/// </summary>
public bool CloseOnClickAway
{
get { return (bool)GetValue(CloseOnClickAwayProperty); }
set { SetValue(CloseOnClickAwayProperty, value); }
}
public static readonly DependencyProperty CloseOnClickAwayParameterProperty = DependencyProperty.Register(
"CloseOnClickAwayParameter", typeof(object), typeof(DialogHost), new PropertyMetadata(default(object)));
/// <summary>
/// Parameter to provide to close handlers if an close due to click away is instigated.
/// </summary>
public object CloseOnClickAwayParameter
{
get { return (object)GetValue(CloseOnClickAwayParameterProperty); }
set { SetValue(CloseOnClickAwayParameterProperty, value); }
}
public static readonly DependencyProperty SnackbarMessageQueueProperty = DependencyProperty.Register(
"SnackbarMessageQueue", typeof(SnackbarMessageQueue), typeof(DialogHost), new PropertyMetadata(default(SnackbarMessageQueue), SnackbarMessageQueuePropertyChangedCallback));
private static void SnackbarMessageQueuePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var dialogHost = (DialogHost)dependencyObject;
if (dialogHost._currentSnackbarMessageQueueUnPauseAction != null)
{
dialogHost._currentSnackbarMessageQueueUnPauseAction();
dialogHost._currentSnackbarMessageQueueUnPauseAction = null;
}
if (!dialogHost.IsOpen) return;
var snackbarMessageQueue = dependencyPropertyChangedEventArgs.NewValue as SnackbarMessageQueue;
dialogHost._currentSnackbarMessageQueueUnPauseAction = snackbarMessageQueue?.Pause();
}
/// <summary>
/// Allows association of a snackbar, so that notifications can be paused whilst a dialog is being displayed.
/// </summary>
public SnackbarMessageQueue SnackbarMessageQueue
{
get { return (SnackbarMessageQueue)GetValue(SnackbarMessageQueueProperty); }
set { SetValue(SnackbarMessageQueueProperty, value); }
}
public static readonly DependencyProperty DialogThemeProperty =
DependencyProperty.Register(nameof(DialogTheme), typeof(BaseTheme), typeof(DialogHost), new PropertyMetadata(default(BaseTheme)));
/// <summary>
/// Set the theme (light/dark) for the dialog.
/// </summary>
public BaseTheme DialogTheme
{
get { return (BaseTheme)GetValue(DialogThemeProperty); }
set { SetValue(DialogThemeProperty, value); }
}
public static readonly DependencyProperty PopupStyleProperty = DependencyProperty.Register(
nameof(PopupStyle), typeof(Style), typeof(DialogHost), new PropertyMetadata(default(Style)));
public Style PopupStyle
{
get { return (Style)GetValue(PopupStyleProperty); }
set { SetValue(PopupStyleProperty, value); }
}
public override void OnApplyTemplate()
{
if (_contentCoverGrid != null)
_contentCoverGrid.MouseLeftButtonUp -= ContentCoverGridOnMouseLeftButtonUp;
_popup = GetTemplateChild(PopupPartName) as Popup;
_popupContentControl = GetTemplateChild(PopupContentPartName) as ContentControl;
_contentCoverGrid = GetTemplateChild(ContentCoverGridName) as Grid;
if (_contentCoverGrid != null)
_contentCoverGrid.MouseLeftButtonUp += ContentCoverGridOnMouseLeftButtonUp;
VisualStateManager.GoToState(this, SelectState(), false);
base.OnApplyTemplate();
}
#region open dialog events/callbacks
public static readonly RoutedEvent DialogOpenedEvent =
EventManager.RegisterRoutedEvent(
"DialogOpened",
RoutingStrategy.Bubble,
typeof(DialogOpenedEventHandler),
typeof(DialogHost));
/// <summary>
/// Raised when a dialog is opened.
/// </summary>
public event DialogOpenedEventHandler DialogOpened
{
add { AddHandler(DialogOpenedEvent, value); }
remove { RemoveHandler(DialogOpenedEvent, value); }
}
/// <summary>
/// Attached property which can be used on the <see cref="Button"/> which instigated the <see cref="OpenDialogCommand"/> to process the event.
/// </summary>
public static readonly DependencyProperty DialogOpenedAttachedProperty = DependencyProperty.RegisterAttached(
"DialogOpenedAttached", typeof(DialogOpenedEventHandler), typeof(DialogHost), new PropertyMetadata(default(DialogOpenedEventHandler)));
public static void SetDialogOpenedAttached(DependencyObject element, DialogOpenedEventHandler value)
{
element.SetValue(DialogOpenedAttachedProperty, value);
}
public static DialogOpenedEventHandler GetDialogOpenedAttached(DependencyObject element)
{
return (DialogOpenedEventHandler)element.GetValue(DialogOpenedAttachedProperty);
}
public static readonly DependencyProperty DialogOpenedCallbackProperty = DependencyProperty.Register(
nameof(DialogOpenedCallback), typeof(DialogOpenedEventHandler), typeof(DialogHost), new PropertyMetadata(default(DialogOpenedEventHandler)));
/// <summary>
/// Callback fired when the <see cref="DialogOpened"/> event is fired, allowing the event to be processed from a binding/view model.
/// </summary>
public DialogOpenedEventHandler DialogOpenedCallback
{
get { return (DialogOpenedEventHandler)GetValue(DialogOpenedCallbackProperty); }
set { SetValue(DialogOpenedCallbackProperty, value); }
}
protected void OnDialogOpened(DialogOpenedEventArgs eventArgs)
{
RaiseEvent(eventArgs);
}
#endregion
#region close dialog events/callbacks
public static readonly RoutedEvent DialogClosingEvent =
EventManager.RegisterRoutedEvent(
"DialogClosing",
RoutingStrategy.Bubble,
typeof(DialogClosingEventHandler),
typeof(DialogHost));
/// <summary>
/// Raised just before a dialog is closed.
/// </summary>
public event DialogClosingEventHandler DialogClosing
{
add { AddHandler(DialogClosingEvent, value); }
remove { RemoveHandler(DialogClosingEvent, value); }
}
/// <summary>
/// Attached property which can be used on the <see cref="Button"/> which instigated the <see cref="OpenDialogCommand"/> to process the closing event.
/// </summary>
public static readonly DependencyProperty DialogClosingAttachedProperty = DependencyProperty.RegisterAttached(
"DialogClosingAttached", typeof(DialogClosingEventHandler), typeof(DialogHost), new PropertyMetadata(default(DialogClosingEventHandler)));
public static void SetDialogClosingAttached(DependencyObject element, DialogClosingEventHandler value)
{
element.SetValue(DialogClosingAttachedProperty, value);
}
public static DialogClosingEventHandler GetDialogClosingAttached(DependencyObject element)
{
return (DialogClosingEventHandler)element.GetValue(DialogClosingAttachedProperty);
}
public static readonly DependencyProperty DialogClosingCallbackProperty = DependencyProperty.Register(
nameof(DialogClosingCallback), typeof(DialogClosingEventHandler), typeof(DialogHost), new PropertyMetadata(default(DialogClosingEventHandler)));
/// <summary>
/// Callback fired when the <see cref="DialogClosing"/> event is fired, allowing the event to be processed from a binding/view model.
/// </summary>
public DialogClosingEventHandler DialogClosingCallback
{
get { return (DialogClosingEventHandler)GetValue(DialogClosingCallbackProperty); }
set { SetValue(DialogClosingCallbackProperty, value); }
}
protected void OnDialogClosing(DialogClosingEventArgs eventArgs)
{
RaiseEvent(eventArgs);
}
#endregion
internal void AssertTargetableContent()
{
var existindBinding = BindingOperations.GetBindingExpression(this, DialogContentProperty);
if (existindBinding != null)
throw new InvalidOperationException(
"Content cannot be passed to a dialog via the OpenDialog if DialogContent already has a binding.");
}
internal void Close(object parameter)
{
var dialogClosingEventArgs = new DialogClosingEventArgs(_session, parameter, DialogClosingEvent);
_session.IsEnded = true;
//multiple ways of calling back that the dialog is closing:
// * routed event
// * the attached property (which should be applied to the button which opened the dialog
// * straight forward dependency property
// * handler provided to the async show method
OnDialogClosing(dialogClosingEventArgs);
_attachedDialogClosingEventHandler?.Invoke(this, dialogClosingEventArgs);
DialogClosingCallback?.Invoke(this, dialogClosingEventArgs);
_asyncShowClosingEventHandler?.Invoke(this, dialogClosingEventArgs);
if (!dialogClosingEventArgs.IsCancelled)
SetCurrentValue(IsOpenProperty, false);
else
_session.IsEnded = false;
_closeDialogExecutionParameter = parameter;
}
/// <summary>
/// Attempts to focus the content of a popup.
/// </summary>
/// <returns>The popup content.</returns>
internal UIElement FocusPopup()
{
var child = _popup?.Child;
if (child == null) return null;
CommandManager.InvalidateRequerySuggested();
var focusable = child.VisualDepthFirstTraversal().OfType<UIElement>().FirstOrDefault(ui => ui.Focusable && ui.IsVisible);
focusable?.Dispatcher.InvokeAsync(() =>
{
if (!focusable.Focus()) return;
focusable.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}, DispatcherPriority.Background);
return child;
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
var window = Window.GetWindow(this);
if (window != null && !window.IsActive)
window.Activate();
base.OnPreviewMouseDown(e);
}
private void ContentCoverGridOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
if (CloseOnClickAway)
Close(CloseOnClickAwayParameter);
}
private void OpenDialogHandler(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
if (executedRoutedEventArgs.Handled) return;
var dependencyObject = executedRoutedEventArgs.OriginalSource as DependencyObject;
if (dependencyObject != null)
{
_attachedDialogOpenedEventHandler = GetDialogOpenedAttached(dependencyObject);
_attachedDialogClosingEventHandler = GetDialogClosingAttached(dependencyObject);
}
if (executedRoutedEventArgs.Parameter != null)
{
AssertTargetableContent();
if (_popupContentControl != null)
{
switch (OpenDialogCommandDataContextSource)
{
case DialogHostOpenDialogCommandDataContextSource.SenderElement:
_popupContentControl.DataContext =
(executedRoutedEventArgs.OriginalSource as FrameworkElement)?.DataContext;
break;
case DialogHostOpenDialogCommandDataContextSource.DialogHostInstance:
_popupContentControl.DataContext = DataContext;
break;
case DialogHostOpenDialogCommandDataContextSource.None:
_popupContentControl.DataContext = null;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
DialogContent = executedRoutedEventArgs.Parameter;
}
SetCurrentValue(IsOpenProperty, true);
executedRoutedEventArgs.Handled = true;
}
private void CloseDialogCanExecute(object sender, CanExecuteRoutedEventArgs canExecuteRoutedEventArgs)
{
canExecuteRoutedEventArgs.CanExecute = _session != null;
}
private void CloseDialogHandler(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
if (executedRoutedEventArgs.Handled) return;
Close(executedRoutedEventArgs.Parameter);
executedRoutedEventArgs.Handled = true;
}
private string SelectState()
{
return IsOpen ? OpenStateName : ClosedStateName;
}
private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
{
LoadedInstances.Remove(this);
SetCurrentValue(IsOpenProperty, false);
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
LoadedInstances.Add(this);
}
private static void WatchWindowActivation(DialogHost dialogHost)
{
var window = Window.GetWindow(dialogHost);
if (window != null)
{
window.Activated += dialogHost.WindowOnActivated;
window.Deactivated += dialogHost.WindowOnDeactivated;
dialogHost._closeCleanUp = () =>
{
window.Activated -= dialogHost.WindowOnActivated;
window.Deactivated -= dialogHost.WindowOnDeactivated;
};
}
else
{
dialogHost._closeCleanUp = () => { };
}
}
private void WindowOnDeactivated(object sender, EventArgs eventArgs)
{
_restoreFocusWindowReactivation = _popup != null ? FocusManager.GetFocusedElement((Window)sender) : null;
}
private void WindowOnActivated(object sender, EventArgs eventArgs)
{
if (_restoreFocusWindowReactivation != null)
{
Dispatcher.BeginInvoke(new Action(() =>
{
Keyboard.Focus(_restoreFocusWindowReactivation);
}));
}
}
}
}
@@ -0,0 +1,169 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace MaterialDesignThemes.Wpf
{
/// <summary>
/// Helper extensions for showing dialogs.
/// </summary>
public static class DialogHostEx
{
/// <summary>
/// Shows a dialog using the first found <see cref="DialogHost"/> in a given <see cref="Window"/>.
/// </summary>
/// <param name="window">Window on which the modal dialog should be displayed. Must contain a <see cref="DialogHost"/>.</param>
/// <param name="content"></param>
/// <exception cref="InvalidOperationException">
/// Thrown is a <see cref="DialogHost"/> is not found when conducting a depth first traversal of visual tree.
/// </exception>
/// <remarks>
/// As a depth first traversal of the window's visual tree is performed, it is not safe to use this method in a situtation where a screen has multiple <see cref="DialogHost"/>s.
/// </remarks>
/// <returns></returns>
public static async Task<object> ShowDialog(this Window window, object content)
{
return await GetFirstDialogHost(window).ShowInternal(content, null, null);
}
/// <summary>
/// Shows a dialog using the first found <see cref="DialogHost"/> in a given <see cref="Window"/>.
/// </summary>
/// <param name="window">Window on which the modal dialog should be displayed. Must contain a <see cref="DialogHost"/>.</param>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="openedEventHandler">Allows access to opened event which would otherwise have been subscribed to on a instance.</param>
/// <exception cref="InvalidOperationException">
/// Thrown is a <see cref="DialogHost"/> is not found when conducting a depth first traversal of visual tree.
/// </exception>
/// <remarks>
/// As a depth first traversal of the window's visual tree is performed, it is not safe to use this method in a situtation where a screen has multiple <see cref="DialogHost"/>s.
/// </remarks>
/// <returns></returns>
public static async Task<object> ShowDialog(this Window window, object content, DialogOpenedEventHandler openedEventHandler)
{
return await GetFirstDialogHost(window).ShowInternal(content, openedEventHandler, null);
}
/// <summary>
/// Shows a dialog using the first found <see cref="DialogHost"/> in a given <see cref="Window"/>.
/// </summary>
/// <param name="window">Window on which the modal dialog should be displayed. Must contain a <see cref="DialogHost"/>.</param>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="closingEventHandler">Allows access to closing event which would otherwise have been subscribed to on a instance.</param>
/// <exception cref="InvalidOperationException">
/// Thrown is a <see cref="DialogHost"/> is not found when conducting a depth first traversal of visual tree.
/// </exception>
/// <remarks>
/// As a depth first traversal of the window's visual tree is performed, it is not safe to use this method in a situtation where a screen has multiple <see cref="DialogHost"/>s.
/// </remarks>
/// <returns></returns>
public static async Task<object> ShowDialog(this Window window, object content, DialogClosingEventHandler closingEventHandler)
{
return await GetFirstDialogHost(window).ShowInternal(content, null, closingEventHandler);
}
/// <summary>
/// Shows a dialog using the first found <see cref="DialogHost"/> in a given <see cref="Window"/>.
/// </summary>
/// <param name="window">Window on which the modal dialog should be displayed. Must contain a <see cref="DialogHost"/>.</param>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="openedEventHandler">Allows access to opened event which would otherwise have been subscribed to on a instance.</param>
/// <param name="closingEventHandler">Allows access to closing event which would otherwise have been subscribed to on a instance.</param>
/// <exception cref="InvalidOperationException">
/// Thrown is a <see cref="DialogHost"/> is not found when conducting a depth first traversal of visual tree.
/// </exception>
/// <remarks>
/// As a depth first traversal of the window's visual tree is performed, it is not safe to use this method in a situtation where a screen has multiple <see cref="DialogHost"/>s.
/// </remarks>
/// <returns></returns>
public static async Task<object> ShowDialog(this Window window, object content, DialogOpenedEventHandler openedEventHandler, DialogClosingEventHandler closingEventHandler)
{
return await GetFirstDialogHost(window).ShowInternal(content, openedEventHandler, closingEventHandler);
}
/// <summary>
/// Shows a dialog using the parent/ancestor <see cref="DialogHost"/> of the a given <see cref="DependencyObject"/>.
/// </summary>
/// <param name="childDependencyObject">Dependency object which should be a visual child of a <see cref="DialogHost"/>, where the dialog will be shown.</param>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <exception cref="InvalidOperationException">
/// Thrown is a <see cref="DialogHost"/> is not found when conducting a depth first traversal of visual tree.
/// </exception>
/// <returns></returns>
public static async Task<object> ShowDialog(this DependencyObject childDependencyObject, object content)
{
return await GetOwningDialogHost(childDependencyObject).ShowInternal(content, null, null);
}
/// <summary>
/// Shows a dialog using the parent/ancestor <see cref="DialogHost"/> of the a given <see cref="DependencyObject"/>.
/// </summary>
/// <param name="childDependencyObject">Dependency object which should be a visual child of a <see cref="DialogHost"/>, where the dialog will be shown.</param>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="openedEventHandler">Allows access to opened event which would otherwise have been subscribed to on a instance.</param>
/// <exception cref="InvalidOperationException">
/// Thrown is a <see cref="DialogHost"/> is not found when conducting a depth first traversal of visual tree.
/// </exception>
/// <returns></returns>
public static async Task<object> ShowDialog(this DependencyObject childDependencyObject, object content, DialogOpenedEventHandler openedEventHandler)
{
return await GetOwningDialogHost(childDependencyObject).ShowInternal(content, openedEventHandler, null);
}
/// <summary>
/// Shows a dialog using the parent/ancestor <see cref="DialogHost"/> of the a given <see cref="DependencyObject"/>.
/// </summary>
/// <param name="childDependencyObject">Dependency object which should be a visual child of a <see cref="DialogHost"/>, where the dialog will be shown.</param>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="closingEventHandler">Allows access to closing event which would otherwise have been subscribed to on a instance.</param>
/// <exception cref="InvalidOperationException">
/// Thrown is a <see cref="DialogHost"/> is not found when conducting a depth first traversal of visual tree.
/// </exception>
/// <returns></returns>
public static async Task<object> ShowDialog(this DependencyObject childDependencyObject, object content, DialogClosingEventHandler closingEventHandler)
{
return await GetOwningDialogHost(childDependencyObject).ShowInternal(content, null, closingEventHandler);
}
/// <summary>
/// Shows a dialog using the parent/ancestor <see cref="DialogHost"/> of the a given <see cref="DependencyObject"/>.
/// </summary>
/// <param name="childDependencyObject">Dependency object which should be a visual child of a <see cref="DialogHost"/>, where the dialog will be shown.</param>
/// <param name="content">Content to show (can be a control or view model).</param>
/// <param name="openedEventHandler">Allows access to opened event which would otherwise have been subscribed to on a instance.</param>
/// <param name="closingEventHandler">Allows access to closing event which would otherwise have been subscribed to on a instance.</param>
/// <exception cref="InvalidOperationException">
/// Thrown is a <see cref="DialogHost"/> is not found when conducting a depth first traversal of visual tree.
/// </exception>
/// <returns></returns>
public static async Task<object> ShowDialog(this DependencyObject childDependencyObject, object content, DialogOpenedEventHandler openedEventHandler, DialogClosingEventHandler closingEventHandler)
{
return await GetOwningDialogHost(childDependencyObject).ShowInternal(content, openedEventHandler, closingEventHandler);
}
private static DialogHost GetFirstDialogHost(Window window)
{
if (window == null) throw new ArgumentNullException(nameof(window));
var dialogHost = window.VisualDepthFirstTraversal().OfType<DialogHost>().FirstOrDefault();
if (dialogHost == null)
throw new InvalidOperationException("Unable to find a DialogHost in visual tree");
return dialogHost;
}
private static DialogHost GetOwningDialogHost(DependencyObject childDependencyObject)
{
if (childDependencyObject == null) throw new ArgumentNullException(nameof(childDependencyObject));
var dialogHost = childDependencyObject.GetVisualAncestry().OfType<DialogHost>().FirstOrDefault();
if (dialogHost == null)
throw new InvalidOperationException("Unable to find a DialogHost in visual tree ancestory");
return dialogHost;
}
}
}
@@ -0,0 +1,21 @@
using System;
using System.Windows;
namespace MaterialDesignThemes.Wpf
{
public class DialogOpenedEventArgs : RoutedEventArgs
{
public DialogOpenedEventArgs(DialogSession session, RoutedEvent routedEvent)
{
if (session == null) throw new ArgumentNullException(nameof(session));
Session = session;
RoutedEvent = routedEvent;
}
/// <summary>
/// Allows interation with the current dialog session.
/// </summary>
public DialogSession Session { get; }
}
}
@@ -0,0 +1,4 @@
namespace MaterialDesignThemes.Wpf
{
public delegate void DialogOpenedEventHandler(object sender, DialogOpenedEventArgs eventArgs);
}
@@ -0,0 +1,70 @@
using System;
using System.Windows.Threading;
namespace MaterialDesignThemes.Wpf
{
/// <summary>
/// Allows an open dialog to be managed. Use is only permitted during a single display operation.
/// </summary>
public class DialogSession
{
private readonly DialogHost _owner;
internal DialogSession(DialogHost owner)
{
if (owner == null) throw new ArgumentNullException(nameof(owner));
_owner = owner;
}
/// <summary>
/// Indicates if the dialog session has ended. Once ended no further method calls will be permitted.
/// </summary>
/// <remarks>
/// Client code cannot set this directly, this is internally managed. To end the dicalog session use <see cref="Close()"/>.
/// </remarks>
public bool IsEnded { get; internal set; }
/// <summary>
/// Gets the <see cref="DialogHost.DialogContent"/> which is currently displayed, so this could be a view model or a UI element.
/// </summary>
public object Content => _owner.DialogContent;
/// <summary>
/// Update the currrent content in the dialog.
/// </summary>
/// <param name="content"></param>
public void UpdateContent(object content)
{
_owner.AssertTargetableContent();
_owner.DialogContent = content ?? throw new ArgumentNullException(nameof(content));
_owner.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
_owner.FocusPopup();
}));
}
/// <summary>
/// Closes the dialog.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the dialog session has ended, or a close operation is currently in progress.</exception>
public void Close()
{
if (IsEnded) throw new InvalidOperationException("Dialog session has ended.");
_owner.Close(null);
}
/// <summary>
/// Closes the dialog.
/// </summary>
/// <param name="parameter">Result parameter which will be returned in <see cref="DialogClosingEventArgs.Parameter"/> or from <see cref="DialogHost.Show(object)"/> method.</param>
/// <exception cref="InvalidOperationException">Thrown if the dialog session has ended, or a close operation is currently in progress.</exception>
public void Close(object parameter)
{
if (IsEnded) throw new InvalidOperationException("Dialog session has ended.");
_owner.Close(parameter);
}
}
}
@@ -0,0 +1,50 @@
using System.Reflection;
using System.Windows;
using System.Windows.Media;
namespace MaterialDesignThemes.Wpf
{
internal static class DpiHelper
{
private static readonly int DpiX;
private static readonly int DpiY;
private const double StandardDpiX = 96.0;
private const double StandardDpiY = 96.0;
static DpiHelper()
{
var dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic | BindingFlags.Static);
var dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", BindingFlags.NonPublic | BindingFlags.Static);
DpiX = (int)dpiXProperty.GetValue(null, null);
DpiY = (int)dpiYProperty.GetValue(null, null);
}
public static double TransformToDeviceY(Visual visual, double y)
{
var source = PresentationSource.FromVisual(visual);
if (source?.CompositionTarget != null) return y * source.CompositionTarget.TransformToDevice.M22;
return TransformToDeviceY(y);
}
public static double TransformToDeviceX(Visual visual, double x)
{
var source = PresentationSource.FromVisual(visual);
if (source?.CompositionTarget != null) return x * source.CompositionTarget.TransformToDevice.M11;
return TransformToDeviceX(x);
}
public static double TransformToDeviceY(double y)
{
return y * DpiY / StandardDpiY;
}
public static double TransformToDeviceX(double x)
{
return x * DpiX / StandardDpiX;
}
}
}
@@ -0,0 +1,532 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using MaterialDesignThemes.Wpf.Transitions;
using System.Collections.Generic;
namespace MaterialDesignThemes.Wpf
{
[TemplateVisualState(GroupName = TemplateAllDrawersGroupName, Name = TemplateAllDrawersAllClosedStateName)]
[TemplateVisualState(GroupName = TemplateAllDrawersGroupName, Name = TemplateAllDrawersAnyOpenStateName)]
[TemplateVisualState(GroupName = TemplateLeftDrawerGroupName, Name = TemplateLeftClosedStateName)]
[TemplateVisualState(GroupName = TemplateLeftDrawerGroupName, Name = TemplateLeftOpenStateName)]
[TemplateVisualState(GroupName = TemplateTopDrawerGroupName, Name = TemplateTopClosedStateName)]
[TemplateVisualState(GroupName = TemplateTopDrawerGroupName, Name = TemplateTopOpenStateName)]
[TemplateVisualState(GroupName = TemplateRightDrawerGroupName, Name = TemplateRightClosedStateName)]
[TemplateVisualState(GroupName = TemplateRightDrawerGroupName, Name = TemplateRightOpenStateName)]
[TemplateVisualState(GroupName = TemplateBottomDrawerGroupName, Name = TemplateBottomClosedStateName)]
[TemplateVisualState(GroupName = TemplateBottomDrawerGroupName, Name = TemplateBottomOpenStateName)]
[TemplatePart(Name = TemplateContentCoverPartName, Type = typeof(FrameworkElement))]
[TemplatePart(Name = TemplateLeftDrawerPartName, Type = typeof(FrameworkElement))]
[TemplatePart(Name = TemplateTopDrawerPartName, Type = typeof(FrameworkElement))]
[TemplatePart(Name = TemplateRightDrawerPartName, Type = typeof(FrameworkElement))]
[TemplatePart(Name = TemplateBottomDrawerPartName, Type = typeof(FrameworkElement))]
public class DrawerHost : ContentControl
{
public const string TemplateAllDrawersGroupName = "AllDrawers";
public const string TemplateAllDrawersAllClosedStateName = "AllClosed";
public const string TemplateAllDrawersAnyOpenStateName = "AnyOpen";
public const string TemplateLeftDrawerGroupName = "LeftDrawer";
public const string TemplateLeftClosedStateName = "LeftDrawerClosed";
public const string TemplateLeftOpenStateName = "LeftDrawerOpen";
public const string TemplateTopDrawerGroupName = "TopDrawer";
public const string TemplateTopClosedStateName = "TopDrawerClosed";
public const string TemplateTopOpenStateName = "TopDrawerOpen";
public const string TemplateRightDrawerGroupName = "RightDrawer";
public const string TemplateRightClosedStateName = "RightDrawerClosed";
public const string TemplateRightOpenStateName = "RightDrawerOpen";
public const string TemplateBottomDrawerGroupName = "BottomDrawer";
public const string TemplateBottomClosedStateName = "BottomDrawerClosed";
public const string TemplateBottomOpenStateName = "BottomDrawerOpen";
public const string TemplateContentCoverPartName = "PART_ContentCover";
public const string TemplateLeftDrawerPartName = "PART_LeftDrawer";
public const string TemplateTopDrawerPartName = "PART_TopDrawer";
public const string TemplateRightDrawerPartName = "PART_RightDrawer";
public const string TemplateBottomDrawerPartName = "PART_BottomDrawer";
public static RoutedCommand OpenDrawerCommand = new RoutedCommand();
public static RoutedCommand CloseDrawerCommand = new RoutedCommand();
private FrameworkElement _templateContentCoverElement;
private FrameworkElement _leftDrawerElement;
private FrameworkElement _topDrawerElement;
private FrameworkElement _rightDrawerElement;
private FrameworkElement _bottomDrawerElement;
private bool _lockZIndexes;
private readonly IDictionary<DependencyProperty, DependencyPropertyKey> _zIndexPropertyLookup = new Dictionary<DependencyProperty, DependencyPropertyKey>
{
{ IsLeftDrawerOpenProperty, LeftDrawerZIndexPropertyKey },
{ IsTopDrawerOpenProperty, TopDrawerZIndexPropertyKey },
{ IsRightDrawerOpenProperty, RightDrawerZIndexPropertyKey },
{ IsBottomDrawerOpenProperty, BottomDrawerZIndexPropertyKey }
};
static DrawerHost()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DrawerHost), new FrameworkPropertyMetadata(typeof(DrawerHost)));
}
public DrawerHost()
{
CommandBindings.Add(new CommandBinding(OpenDrawerCommand, OpenDrawerHandler));
CommandBindings.Add(new CommandBinding(CloseDrawerCommand, CloseDrawerHandler));
}
#region top drawer
public static readonly DependencyProperty TopDrawerContentProperty = DependencyProperty.Register(
nameof(TopDrawerContent), typeof(object), typeof(DrawerHost), new PropertyMetadata(default(object)));
public object TopDrawerContent
{
get { return (object)GetValue(TopDrawerContentProperty); }
set { SetValue(TopDrawerContentProperty, value); }
}
public static readonly DependencyProperty TopDrawerContentTemplateProperty = DependencyProperty.Register(
nameof(TopDrawerContentTemplate), typeof(DataTemplate), typeof(DrawerHost), new PropertyMetadata(default(DataTemplate)));
public DataTemplate TopDrawerContentTemplate
{
get { return (DataTemplate)GetValue(TopDrawerContentTemplateProperty); }
set { SetValue(TopDrawerContentTemplateProperty, value); }
}
public static readonly DependencyProperty TopDrawerContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(TopDrawerContentTemplateSelector), typeof(DataTemplateSelector), typeof(DrawerHost), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector TopDrawerContentTemplateSelector
{
get { return (DataTemplateSelector)GetValue(TopDrawerContentTemplateSelectorProperty); }
set { SetValue(TopDrawerContentTemplateSelectorProperty, value); }
}
public static readonly DependencyProperty TopDrawerContentStringFormatProperty = DependencyProperty.Register(
nameof(TopDrawerContentStringFormat), typeof(string), typeof(DrawerHost), new PropertyMetadata(default(string)));
public string TopDrawerContentStringFormat
{
get { return (string)GetValue(TopDrawerContentStringFormatProperty); }
set { SetValue(TopDrawerContentStringFormatProperty, value); }
}
public static readonly DependencyProperty TopDrawerBackgroundProperty = DependencyProperty.Register(
nameof(TopDrawerBackground), typeof(Brush), typeof(DrawerHost), new PropertyMetadata(default(Brush)));
public Brush TopDrawerBackground
{
get { return (Brush)GetValue(TopDrawerBackgroundProperty); }
set { SetValue(TopDrawerBackgroundProperty, value); }
}
public static readonly DependencyProperty IsTopDrawerOpenProperty = DependencyProperty.Register(
nameof(IsTopDrawerOpen), typeof(bool), typeof(DrawerHost), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsDrawerOpenPropertyChangedCallback));
public bool IsTopDrawerOpen
{
get { return (bool)GetValue(IsTopDrawerOpenProperty); }
set { SetValue(IsTopDrawerOpenProperty, value); }
}
private static readonly DependencyPropertyKey TopDrawerZIndexPropertyKey =
DependencyProperty.RegisterReadOnly(
"TopDrawerZIndex", typeof(int), typeof(DrawerHost),
new PropertyMetadata(4));
public static readonly DependencyProperty TopDrawerZIndexProperty = TopDrawerZIndexPropertyKey.DependencyProperty;
public int TopDrawerZIndex
{
get { return (int)GetValue(TopDrawerZIndexProperty); }
private set { SetValue(TopDrawerZIndexPropertyKey, value); }
}
#endregion
#region left drawer
public static readonly DependencyProperty LeftDrawerContentProperty = DependencyProperty.Register(
nameof(LeftDrawerContent), typeof (object), typeof (DrawerHost), new PropertyMetadata(default(object)));
public object LeftDrawerContent
{
get { return (object) GetValue(LeftDrawerContentProperty); }
set { SetValue(LeftDrawerContentProperty, value); }
}
public static readonly DependencyProperty LeftDrawerContentTemplateProperty = DependencyProperty.Register(
nameof(LeftDrawerContentTemplate), typeof (DataTemplate), typeof (DrawerHost), new PropertyMetadata(default(DataTemplate)));
public DataTemplate LeftDrawerContentTemplate
{
get { return (DataTemplate) GetValue(LeftDrawerContentTemplateProperty); }
set { SetValue(LeftDrawerContentTemplateProperty, value); }
}
public static readonly DependencyProperty LeftDrawerContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(LeftDrawerContentTemplateSelector), typeof (DataTemplateSelector), typeof (DrawerHost), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector LeftDrawerContentTemplateSelector
{
get { return (DataTemplateSelector) GetValue(LeftDrawerContentTemplateSelectorProperty); }
set { SetValue(LeftDrawerContentTemplateSelectorProperty, value); }
}
public static readonly DependencyProperty LeftDrawerContentStringFormatProperty = DependencyProperty.Register(
nameof(LeftDrawerContentStringFormat), typeof (string), typeof (DrawerHost), new PropertyMetadata(default(string)));
public string LeftDrawerContentStringFormat
{
get { return (string) GetValue(LeftDrawerContentStringFormatProperty); }
set { SetValue(LeftDrawerContentStringFormatProperty, value); }
}
public static readonly DependencyProperty LeftDrawerBackgroundProperty = DependencyProperty.Register(
nameof(LeftDrawerBackground), typeof (Brush), typeof (DrawerHost), new PropertyMetadata(default(Brush)));
public Brush LeftDrawerBackground
{
get { return (Brush) GetValue(LeftDrawerBackgroundProperty); }
set { SetValue(LeftDrawerBackgroundProperty, value); }
}
public static readonly DependencyProperty IsLeftDrawerOpenProperty = DependencyProperty.Register(
nameof(IsLeftDrawerOpen), typeof (bool), typeof (DrawerHost), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsDrawerOpenPropertyChangedCallback));
public bool IsLeftDrawerOpen
{
get { return (bool) GetValue(IsLeftDrawerOpenProperty); }
set { SetValue(IsLeftDrawerOpenProperty, value); }
}
private static readonly DependencyPropertyKey LeftDrawerZIndexPropertyKey =
DependencyProperty.RegisterReadOnly(
"LeftDrawerZIndex", typeof(int), typeof(DrawerHost),
new PropertyMetadata(2));
public static readonly DependencyProperty LeftDrawerZIndexProperty = LeftDrawerZIndexPropertyKey.DependencyProperty;
public int LeftDrawerZIndex
{
get { return (int)GetValue(LeftDrawerZIndexProperty); }
private set { SetValue(LeftDrawerZIndexPropertyKey, value); }
}
#endregion
#region right drawer
public static readonly DependencyProperty RightDrawerContentProperty = DependencyProperty.Register(
nameof(RightDrawerContent), typeof(object), typeof(DrawerHost), new PropertyMetadata(default(object)));
public object RightDrawerContent
{
get { return (object)GetValue(RightDrawerContentProperty); }
set { SetValue(RightDrawerContentProperty, value); }
}
public static readonly DependencyProperty RightDrawerContentTemplateProperty = DependencyProperty.Register(
nameof(RightDrawerContentTemplate), typeof(DataTemplate), typeof(DrawerHost), new PropertyMetadata(default(DataTemplate)));
public DataTemplate RightDrawerContentTemplate
{
get { return (DataTemplate)GetValue(RightDrawerContentTemplateProperty); }
set { SetValue(RightDrawerContentTemplateProperty, value); }
}
public static readonly DependencyProperty RightDrawerContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(RightDrawerContentTemplateSelector), typeof(DataTemplateSelector), typeof(DrawerHost), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector RightDrawerContentTemplateSelector
{
get { return (DataTemplateSelector)GetValue(RightDrawerContentTemplateSelectorProperty); }
set { SetValue(RightDrawerContentTemplateSelectorProperty, value); }
}
public static readonly DependencyProperty RightDrawerContentStringFormatProperty = DependencyProperty.Register(
nameof(RightDrawerContentStringFormat), typeof(string), typeof(DrawerHost), new PropertyMetadata(default(string)));
public string RightDrawerContentStringFormat
{
get { return (string)GetValue(RightDrawerContentStringFormatProperty); }
set { SetValue(RightDrawerContentStringFormatProperty, value); }
}
public static readonly DependencyProperty RightDrawerBackgroundProperty = DependencyProperty.Register(
nameof(RightDrawerBackground), typeof(Brush), typeof(DrawerHost), new PropertyMetadata(default(Brush)));
public Brush RightDrawerBackground
{
get { return (Brush)GetValue(RightDrawerBackgroundProperty); }
set { SetValue(RightDrawerBackgroundProperty, value); }
}
public static readonly DependencyProperty IsRightDrawerOpenProperty = DependencyProperty.Register(
nameof(IsRightDrawerOpen), typeof(bool), typeof(DrawerHost), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsDrawerOpenPropertyChangedCallback));
public bool IsRightDrawerOpen
{
get { return (bool)GetValue(IsRightDrawerOpenProperty); }
set { SetValue(IsRightDrawerOpenProperty, value); }
}
private static readonly DependencyPropertyKey RightDrawerZIndexPropertyKey =
DependencyProperty.RegisterReadOnly(
"RightDrawerZIndex", typeof(int), typeof(DrawerHost),
new PropertyMetadata(1));
public static readonly DependencyProperty RightDrawerZIndexProperty = RightDrawerZIndexPropertyKey.DependencyProperty;
public int RightDrawerZIndex
{
get { return (int)GetValue(RightDrawerZIndexProperty); }
private set { SetValue(RightDrawerZIndexPropertyKey, value); }
}
#endregion
#region bottom drawer
public static readonly DependencyProperty BottomDrawerContentProperty = DependencyProperty.Register(
nameof(BottomDrawerContent), typeof(object), typeof(DrawerHost), new PropertyMetadata(default(object)));
public object BottomDrawerContent
{
get { return (object)GetValue(BottomDrawerContentProperty); }
set { SetValue(BottomDrawerContentProperty, value); }
}
public static readonly DependencyProperty BottomDrawerContentTemplateProperty = DependencyProperty.Register(
nameof(BottomDrawerContentTemplate), typeof(DataTemplate), typeof(DrawerHost), new PropertyMetadata(default(DataTemplate)));
public DataTemplate BottomDrawerContentTemplate
{
get { return (DataTemplate)GetValue(BottomDrawerContentTemplateProperty); }
set { SetValue(BottomDrawerContentTemplateProperty, value); }
}
public static readonly DependencyProperty BottomDrawerContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(BottomDrawerContentTemplateSelector), typeof(DataTemplateSelector), typeof(DrawerHost), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector BottomDrawerContentTemplateSelector
{
get { return (DataTemplateSelector)GetValue(BottomDrawerContentTemplateSelectorProperty); }
set { SetValue(BottomDrawerContentTemplateSelectorProperty, value); }
}
public static readonly DependencyProperty BottomDrawerContentStringFormatProperty = DependencyProperty.Register(
nameof(BottomDrawerContentStringFormat), typeof(string), typeof(DrawerHost), new PropertyMetadata(default(string)));
public string BottomDrawerContentStringFormat
{
get { return (string)GetValue(BottomDrawerContentStringFormatProperty); }
set { SetValue(BottomDrawerContentStringFormatProperty, value); }
}
public static readonly DependencyProperty BottomDrawerBackgroundProperty = DependencyProperty.Register(
nameof(BottomDrawerBackground), typeof(Brush), typeof(DrawerHost), new PropertyMetadata(default(Brush)));
public Brush BottomDrawerBackground
{
get { return (Brush)GetValue(BottomDrawerBackgroundProperty); }
set { SetValue(BottomDrawerBackgroundProperty, value); }
}
public static readonly DependencyProperty IsBottomDrawerOpenProperty = DependencyProperty.Register(
nameof(IsBottomDrawerOpen), typeof(bool), typeof(DrawerHost), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsDrawerOpenPropertyChangedCallback));
public bool IsBottomDrawerOpen
{
get { return (bool)GetValue(IsBottomDrawerOpenProperty); }
set { SetValue(IsBottomDrawerOpenProperty, value); }
}
private static readonly DependencyPropertyKey BottomDrawerZIndexPropertyKey =
DependencyProperty.RegisterReadOnly(
"BottomDrawerZIndex", typeof(int), typeof(DrawerHost),
new PropertyMetadata(3));
public static readonly DependencyProperty BottomDrawerZIndexProperty = BottomDrawerZIndexPropertyKey.DependencyProperty;
public int BottomDrawerZIndex
{
get { return (int)GetValue(BottomDrawerZIndexProperty); }
private set { SetValue(BottomDrawerZIndexPropertyKey, value); }
}
#endregion
public override void OnApplyTemplate()
{
if (_templateContentCoverElement != null)
_templateContentCoverElement.PreviewMouseLeftButtonUp += TemplateContentCoverElementOnPreviewMouseLeftButtonUp;
WireDrawer(_leftDrawerElement, true);
WireDrawer(_topDrawerElement, true);
WireDrawer(_rightDrawerElement, true);
WireDrawer(_bottomDrawerElement, true);
base.OnApplyTemplate();
_templateContentCoverElement = GetTemplateChild(TemplateContentCoverPartName) as FrameworkElement;
if (_templateContentCoverElement != null)
_templateContentCoverElement.PreviewMouseLeftButtonUp += TemplateContentCoverElementOnPreviewMouseLeftButtonUp;
_leftDrawerElement = WireDrawer(GetTemplateChild(TemplateLeftDrawerPartName) as FrameworkElement, false);
_topDrawerElement = WireDrawer(GetTemplateChild(TemplateTopDrawerPartName) as FrameworkElement, false);
_rightDrawerElement = WireDrawer(GetTemplateChild(TemplateRightDrawerPartName) as FrameworkElement, false);
_bottomDrawerElement = WireDrawer(GetTemplateChild(TemplateBottomDrawerPartName) as FrameworkElement, false);
UpdateVisualStates(false);
}
private FrameworkElement WireDrawer(FrameworkElement drawerElement, bool unwire)
{
if (drawerElement == null) return null;
if (unwire)
{
drawerElement.GotFocus -= DrawerElement_GotFocus;
drawerElement.MouseDown -= DrawerElement_MouseDown;
return drawerElement;
}
drawerElement.GotFocus += DrawerElement_GotFocus;
drawerElement.MouseDown += DrawerElement_MouseDown;
return drawerElement;
}
private void DrawerElement_MouseDown(object sender, MouseButtonEventArgs e)
{
ReactToFocus(sender);
}
private void DrawerElement_GotFocus(object sender, RoutedEventArgs e)
{
ReactToFocus(sender);
}
private void ReactToFocus(object sender)
{
if (sender == _leftDrawerElement)
PrepareZIndexes(LeftDrawerZIndexPropertyKey);
else if (sender == _topDrawerElement)
PrepareZIndexes(TopDrawerZIndexPropertyKey);
else if (sender == _rightDrawerElement)
PrepareZIndexes(RightDrawerZIndexPropertyKey);
else if (sender == _bottomDrawerElement)
PrepareZIndexes(BottomDrawerZIndexPropertyKey);
}
private void TemplateContentCoverElementOnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
SetCurrentValue(IsLeftDrawerOpenProperty, false);
SetCurrentValue(IsRightDrawerOpenProperty, false);
SetCurrentValue(IsTopDrawerOpenProperty, false);
SetCurrentValue(IsBottomDrawerOpenProperty, false);
}
private void UpdateVisualStates(bool? useTransitions = null)
{
var anyOpen = IsTopDrawerOpen || IsLeftDrawerOpen || IsBottomDrawerOpen || IsRightDrawerOpen;
VisualStateManager.GoToState(this,
!anyOpen ? TemplateAllDrawersAllClosedStateName : TemplateAllDrawersAnyOpenStateName, useTransitions.HasValue ? useTransitions.Value : !TransitionAssist.GetDisableTransitions(this));
VisualStateManager.GoToState(this,
IsLeftDrawerOpen ? TemplateLeftOpenStateName : TemplateLeftClosedStateName, useTransitions.HasValue ? useTransitions.Value : !TransitionAssist.GetDisableTransitions(this));
VisualStateManager.GoToState(this,
IsTopDrawerOpen ? TemplateTopOpenStateName : TemplateTopClosedStateName, useTransitions.HasValue ? useTransitions.Value : !TransitionAssist.GetDisableTransitions(this));
VisualStateManager.GoToState(this,
IsRightDrawerOpen ? TemplateRightOpenStateName : TemplateRightClosedStateName, useTransitions.HasValue ? useTransitions.Value : !TransitionAssist.GetDisableTransitions(this));
VisualStateManager.GoToState(this,
IsBottomDrawerOpen ? TemplateBottomOpenStateName : TemplateBottomClosedStateName, useTransitions.HasValue ? useTransitions.Value : !TransitionAssist.GetDisableTransitions(this));
}
private static void IsDrawerOpenPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var drawerHost = (DrawerHost)dependencyObject;
if (!drawerHost._lockZIndexes && (bool)dependencyPropertyChangedEventArgs.NewValue)
drawerHost.PrepareZIndexes(drawerHost._zIndexPropertyLookup[dependencyPropertyChangedEventArgs.Property]);
drawerHost.UpdateVisualStates();
}
private void PrepareZIndexes(DependencyPropertyKey zIndexDependencyPropertyKey)
{
var newOrder = new[] { zIndexDependencyPropertyKey }
.Concat(_zIndexPropertyLookup.Values.Except(new[] { zIndexDependencyPropertyKey })
.OrderByDescending(dpk => (int)GetValue(dpk.DependencyProperty)))
.Reverse()
.Select((dpk, idx) => new { dpk, idx });
foreach (var a in newOrder)
SetValue(a.dpk, a.idx);
}
private void CloseDrawerHandler(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
if (executedRoutedEventArgs.Handled) return;
SetOpenFlag(executedRoutedEventArgs, false);
executedRoutedEventArgs.Handled = true;
}
private void OpenDrawerHandler(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
if (executedRoutedEventArgs.Handled) return;
SetOpenFlag(executedRoutedEventArgs, true);
executedRoutedEventArgs.Handled = true;
}
private void SetOpenFlag(ExecutedRoutedEventArgs executedRoutedEventArgs, bool value)
{
if (executedRoutedEventArgs.Parameter is Dock)
{
switch ((Dock)executedRoutedEventArgs.Parameter)
{
case Dock.Left:
SetCurrentValue(IsLeftDrawerOpenProperty, value);
break;
case Dock.Top:
SetCurrentValue(IsTopDrawerOpenProperty, value);
break;
case Dock.Right:
SetCurrentValue(IsRightDrawerOpenProperty, value);
break;
case Dock.Bottom:
SetCurrentValue(IsBottomDrawerOpenProperty, value);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
else
{
try
{
_lockZIndexes = true;
SetCurrentValue(IsLeftDrawerOpenProperty, value);
SetCurrentValue(IsTopDrawerOpenProperty, value);
SetCurrentValue(IsRightDrawerOpenProperty, value);
SetCurrentValue(IsBottomDrawerOpenProperty, value);
}
finally
{
_lockZIndexes = false;
}
}
}
}
}
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
namespace MaterialDesignThemes.Wpf
{
internal static class Extensions
{
public static IEnumerable<DependencyObject> VisualDepthFirstTraversal(this DependencyObject node)
{
if (node == null) throw new ArgumentNullException(nameof(node));
yield return node;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(node); i++)
{
var child = VisualTreeHelper.GetChild(node, i);
foreach (var descendant in child.VisualDepthFirstTraversal())
{
yield return descendant;
}
}
}
public static IEnumerable<DependencyObject> VisualBreadthFirstTraversal(this DependencyObject node)
{
if (node == null) throw new ArgumentNullException(nameof(node));
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(node); i++)
{
var child = VisualTreeHelper.GetChild(node, i);
yield return child;
}
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(node); i++)
{
var child = VisualTreeHelper.GetChild(node, i);
foreach (var descendant in child.VisualDepthFirstTraversal())
{
yield return descendant;
}
}
}
public static bool IsAncestorOf(this DependencyObject parent, DependencyObject node)
{
return node != null && parent.VisualDepthFirstTraversal().Contains(node);
}
/// <summary>
/// Returns full visual ancestry, starting at the leaf.
/// </summary>
/// <param name="leaf"></param>
/// <returns></returns>
public static IEnumerable<DependencyObject> GetVisualAncestry(this DependencyObject leaf)
{
while (leaf != null)
{
yield return leaf;
leaf = VisualTreeHelper.GetParent(leaf);
}
}
public static IEnumerable<DependencyObject> GetLogicalAncestry(this DependencyObject leaf)
{
while (leaf != null)
{
yield return leaf;
leaf = LogicalTreeHelper.GetParent(leaf);
}
}
public static bool IsDescendantOf(this DependencyObject leaf, DependencyObject ancestor)
{
DependencyObject parent = null;
foreach (var node in leaf.GetVisualAncestry())
{
if (Equals(node, ancestor))
return true;
parent = node;
}
return parent.GetLogicalAncestry().Contains(ancestor);
}
}
}
@@ -0,0 +1,188 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
namespace MaterialDesignThemes.Wpf
{
[TemplatePart(Name = Plane3DPartName, Type = typeof(Plane3D))]
[TemplateVisualState(GroupName = TemplateFlipGroupName, Name = TemplateFlippedStateName)]
[TemplateVisualState(GroupName = TemplateFlipGroupName, Name = TemplateUnflippedStateName)]
public class Flipper : Control
{
public static RoutedCommand FlipCommand = new RoutedCommand();
public const string Plane3DPartName = "PART_Plane3D";
public const string TemplateFlipGroupName = "FlipStates";
public const string TemplateFlippedStateName = "Flipped";
public const string TemplateUnflippedStateName = "Unflipped";
private Plane3D _plane3D;
static Flipper()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Flipper), new FrameworkPropertyMetadata(typeof(Flipper)));
}
public Flipper()
{
CommandBindings.Add(new CommandBinding(FlipCommand, FlipHandler));
}
public static readonly DependencyProperty FrontContentProperty = DependencyProperty.Register(
nameof(FrontContent), typeof(object), typeof(Flipper), new PropertyMetadata(default(object)));
public object FrontContent
{
get => GetValue(FrontContentProperty);
set => SetValue(FrontContentProperty, value);
}
public static readonly DependencyProperty FrontContentTemplateProperty = DependencyProperty.Register(
nameof(FrontContentTemplate), typeof(DataTemplate), typeof(Flipper), new PropertyMetadata(default(DataTemplate)));
public DataTemplate FrontContentTemplate
{
get => (DataTemplate) GetValue(FrontContentTemplateProperty);
set => SetValue(FrontContentTemplateProperty, value);
}
public static readonly DependencyProperty FrontContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(FrontContentTemplateSelector), typeof(DataTemplateSelector), typeof(Flipper), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector FrontContentTemplateSelector
{
get => (DataTemplateSelector) GetValue(FrontContentTemplateSelectorProperty);
set => SetValue(FrontContentTemplateSelectorProperty, value);
}
public static readonly DependencyProperty FrontContentStringFormatProperty = DependencyProperty.Register(
nameof(FrontContentStringFormat), typeof(string), typeof(Flipper), new PropertyMetadata(default(string)));
public string FrontContentStringFormat
{
get => (string) GetValue(FrontContentStringFormatProperty);
set => SetValue(FrontContentStringFormatProperty, value);
}
public static readonly DependencyProperty BackContentProperty = DependencyProperty.Register(
nameof(BackContent), typeof(object), typeof(Flipper), new PropertyMetadata(default(object)));
public object BackContent
{
get => (object) GetValue(BackContentProperty);
set => SetValue(BackContentProperty, value);
}
public static readonly DependencyProperty BackContentTemplateProperty = DependencyProperty.Register(
nameof(BackContentTemplate), typeof(DataTemplate), typeof(Flipper), new PropertyMetadata(default(DataTemplate)));
public DataTemplate BackContentTemplate
{
get => (DataTemplate)GetValue(BackContentTemplateProperty);
set => SetValue(BackContentTemplateProperty, value);
}
public static readonly DependencyProperty BackContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(BackContentTemplateSelector), typeof(DataTemplateSelector), typeof(Flipper), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector BackContentTemplateSelector
{
get => (DataTemplateSelector)GetValue(BackContentTemplateSelectorProperty);
set => SetValue(BackContentTemplateSelectorProperty, value);
}
public static readonly DependencyProperty BackContentStringFormatProperty = DependencyProperty.Register(
nameof(BackContentStringFormat), typeof(string), typeof(Flipper), new PropertyMetadata(default(string)));
public string BackContentStringFormat
{
get => (string)GetValue(BackContentStringFormatProperty);
set => SetValue(BackContentStringFormatProperty, value);
}
public static readonly DependencyProperty IsFlippedProperty = DependencyProperty.Register(
nameof(IsFlipped), typeof(bool), typeof(Flipper), new PropertyMetadata(default(bool), IsFlippedPropertyChangedCallback));
private static void IsFlippedPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var flipper = (Flipper)dependencyObject;
flipper.UpdateVisualStates(true);
flipper.RemeasureDuringFlip();
OnIsFlippedChanged(flipper, dependencyPropertyChangedEventArgs);
}
public bool IsFlipped
{
get => (bool) GetValue(IsFlippedProperty);
set => SetValue(IsFlippedProperty, value);
}
public static readonly RoutedEvent IsFlippedChangedEvent =
EventManager.RegisterRoutedEvent(
nameof(IsFlipped),
RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<bool>),
typeof(Flipper));
public event RoutedPropertyChangedEventHandler<bool> IsFlippedChanged
{
add => AddHandler(IsFlippedChangedEvent, value);
remove => RemoveHandler(IsFlippedChangedEvent, value);
}
private static void OnIsFlippedChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = (Flipper)d;
var args = new RoutedPropertyChangedEventArgs<bool>(
(bool)e.OldValue,
(bool)e.NewValue)
{ RoutedEvent = IsFlippedChangedEvent };
instance.RaiseEvent(args);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
UpdateVisualStates(false);
_plane3D = GetTemplateChild(Plane3DPartName) as Plane3D;
}
private void RemeasureDuringFlip()
{
//not entirely happy hardcoding this, but I have explored other options I am not happy with, and this will do for now
const int storyboardMs = 400;
const int granularity = 6;
var remeasureInterval = new TimeSpan(0, 0, 0, 0, storyboardMs/granularity);
var refreshCount = 0;
var plane3D = _plane3D;
if (plane3D == null) return;
DispatcherTimer dt = null;
dt = new DispatcherTimer(remeasureInterval, DispatcherPriority.Normal,
(sender, args) =>
{
plane3D.InvalidateMeasure();
if (refreshCount++ == granularity)
dt.Stop();
}, Dispatcher);
dt.Start();
}
private void UpdateVisualStates(bool useTransitions)
{
VisualStateManager.GoToState(this, IsFlipped ? TemplateFlippedStateName : TemplateUnflippedStateName,
useTransitions);
}
private void FlipHandler(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
SetCurrentValue(IsFlippedProperty, !IsFlipped);
}
}
}
@@ -0,0 +1,132 @@
using System.Windows;
namespace MaterialDesignThemes.Wpf
{
public static class HintAssist
{
#region UseFloating
public static readonly DependencyProperty IsFloatingProperty = DependencyProperty.RegisterAttached(
"IsFloating",
typeof(bool),
typeof(HintAssist),
new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.Inherits));
public static bool GetIsFloating(DependencyObject element)
{
return (bool) element.GetValue(IsFloatingProperty);
}
public static void SetIsFloating(DependencyObject element, bool value)
{
element.SetValue(IsFloatingProperty, value);
}
#endregion
#region FloatingScale & FloatingOffset
public static readonly DependencyProperty FloatingScaleProperty = DependencyProperty.RegisterAttached(
"FloatingScale",
typeof(double),
typeof(HintAssist),
new FrameworkPropertyMetadata(0.74d, FrameworkPropertyMetadataOptions.Inherits));
public static double GetFloatingScale(DependencyObject element)
{
return (double)element.GetValue(FloatingScaleProperty);
}
public static void SetFloatingScale(DependencyObject element, double value)
{
element.SetValue(FloatingScaleProperty, value);
}
public static readonly DependencyProperty FloatingOffsetProperty = DependencyProperty.RegisterAttached(
"FloatingOffset",
typeof(Point),
typeof(HintAssist),
new FrameworkPropertyMetadata(new Point(1, -16), FrameworkPropertyMetadataOptions.Inherits));
public static Point GetFloatingOffset(DependencyObject element)
{
return (Point)element.GetValue(FloatingOffsetProperty);
}
public static void SetFloatingOffset(DependencyObject element, Point value)
{
element.SetValue(FloatingOffsetProperty, value);
}
#endregion
#region Hint
/// <summary>
/// The hint property
/// </summary>
public static readonly DependencyProperty HintProperty = DependencyProperty.RegisterAttached(
"Hint",
typeof(object),
typeof(HintAssist),
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// Sets the hint.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="value">The value.</param>
public static void SetHint(DependencyObject element, object value)
{
element.SetValue(HintProperty, value);
}
/// <summary>
/// Gets the hint.
/// </summary>
/// <param name="element">The element.</param>
/// <returns>
/// The <see cref="string" />.
/// </returns>
public static object GetHint(DependencyObject element)
{
return element.GetValue(HintProperty);
}
#endregion
#region HintOpacity
/// <summary>
/// The hint opacity property
/// </summary>
public static readonly DependencyProperty HintOpacityProperty = DependencyProperty.RegisterAttached(
"HintOpacity",
typeof(double),
typeof(HintAssist),
new PropertyMetadata(.56));
/// <summary>
/// Gets the text box view margin.
/// </summary>
/// <param name="element">The element.</param>
/// <returns>
/// The <see cref="Thickness" />.
/// </returns>
public static double GetHintOpacityProperty(DependencyObject element)
{
return (double)element.GetValue(HintOpacityProperty);
}
/// <summary>
/// Sets the hint opacity.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="value">The value.</param>
public static void SetHintOpacity(DependencyObject element, double value)
{
element.SetValue(HintOpacityProperty, value);
}
#endregion
}
}
@@ -0,0 +1,94 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace MaterialDesignThemes.Wpf
{
public static partial class HintProxyFabric
{
private sealed class ComboBoxHintProxy : IHintProxy
{
private readonly ComboBox _comboBox;
private readonly TextChangedEventHandler _comboBoxTextChangedEventHandler;
public ComboBoxHintProxy(ComboBox comboBox)
{
if (comboBox == null) throw new ArgumentNullException(nameof(comboBox));
_comboBox = comboBox;
_comboBoxTextChangedEventHandler = ComboBoxTextChanged;
_comboBox.AddHandler(TextBoxBase.TextChangedEvent, _comboBoxTextChangedEventHandler);
_comboBox.SelectionChanged += ComboBoxSelectionChanged;
_comboBox.Loaded += ComboBoxLoaded;
_comboBox.IsVisibleChanged += ComboBoxIsVisibleChanged;
_comboBox.IsKeyboardFocusWithinChanged += ComboBoxIsKeyboardFocusWithinChanged;
}
public object Content
{
get
{
if (_comboBox.IsEditable)
{
return _comboBox.Text;
}
var comboBoxItem = _comboBox.SelectedItem as ComboBoxItem;
return comboBoxItem != null
? comboBoxItem.Content
: _comboBox.SelectedItem;
}
}
public bool IsLoaded => _comboBox.IsLoaded;
public bool IsVisible => _comboBox.IsVisible;
public bool IsEmpty() => string.IsNullOrEmpty(_comboBox.Text);
public bool IsFocused() => _comboBox.IsEditable && _comboBox.IsKeyboardFocusWithin;
public event EventHandler ContentChanged;
public event EventHandler IsVisibleChanged;
public event EventHandler Loaded;
public event EventHandler FocusedChanged;
private void ComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_comboBox.Dispatcher.InvokeAsync(() => ContentChanged?.Invoke(sender, EventArgs.Empty));
}
private void ComboBoxIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
IsVisibleChanged?.Invoke(sender, EventArgs.Empty);
}
private void ComboBoxLoaded(object sender, RoutedEventArgs e)
{
Loaded?.Invoke(sender, EventArgs.Empty);
}
private void ComboBoxTextChanged(object sender, TextChangedEventArgs e)
{
ContentChanged?.Invoke(sender, EventArgs.Empty);
}
private void ComboBoxIsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
FocusedChanged?.Invoke(sender, EventArgs.Empty);
}
public void Dispose()
{
_comboBox.RemoveHandler(TextBoxBase.TextChangedEvent, _comboBoxTextChangedEventHandler);
_comboBox.Loaded -= ComboBoxLoaded;
_comboBox.IsVisibleChanged -= ComboBoxIsVisibleChanged;
_comboBox.SelectionChanged -= ComboBoxSelectionChanged;
_comboBox.IsKeyboardFocusWithinChanged -= ComboBoxIsKeyboardFocusWithinChanged;
}
}
}
}
@@ -0,0 +1,68 @@
using System;
using System.Windows.Controls;
namespace MaterialDesignThemes.Wpf
{
public static partial class HintProxyFabric
{
private sealed class PasswordBoxHintProxy : IHintProxy
{
private readonly PasswordBox _passwordBox;
public bool IsEmpty() => string.IsNullOrEmpty(_passwordBox.Password);
public object Content => _passwordBox.Password;
public bool IsLoaded => _passwordBox.IsLoaded;
public bool IsVisible => _passwordBox.IsVisible;
public bool IsFocused() => _passwordBox.IsKeyboardFocused;
public event EventHandler ContentChanged;
public event EventHandler IsVisibleChanged;
public event EventHandler Loaded;
public event EventHandler FocusedChanged;
public PasswordBoxHintProxy(PasswordBox passwordBox)
{
if (passwordBox == null) throw new ArgumentNullException(nameof(passwordBox));
_passwordBox = passwordBox;
_passwordBox.PasswordChanged += PasswordBoxPasswordChanged;
_passwordBox.Loaded += PasswordBoxLoaded;
_passwordBox.IsVisibleChanged += PasswordBoxIsVisibleChanged;
_passwordBox.IsKeyboardFocusedChanged += PasswordBoxIsKeyboardFocusedChanged;
}
private void PasswordBoxIsKeyboardFocusedChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
FocusedChanged?.Invoke(this, EventArgs.Empty);
}
private void PasswordBoxIsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
IsVisibleChanged?.Invoke(this, EventArgs.Empty);
}
private void PasswordBoxLoaded(object sender, System.Windows.RoutedEventArgs e)
{
Loaded?.Invoke(this, EventArgs.Empty);
}
private void PasswordBoxPasswordChanged(object sender, System.Windows.RoutedEventArgs e)
{
ContentChanged?.Invoke(this, EventArgs.Empty);
}
public void Dispose()
{
_passwordBox.PasswordChanged -= PasswordBoxPasswordChanged;
_passwordBox.Loaded -= PasswordBoxLoaded;
_passwordBox.IsVisibleChanged -= PasswordBoxIsVisibleChanged;
_passwordBox.IsKeyboardFocusedChanged -= PasswordBoxIsKeyboardFocusedChanged;
}
}
}
}
@@ -0,0 +1,68 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace MaterialDesignThemes.Wpf
{
public static partial class HintProxyFabric
{
private sealed class TextBoxHintProxy : IHintProxy
{
private readonly TextBox _textBox;
public object Content => _textBox.Text;
public bool IsLoaded => _textBox.IsLoaded;
public bool IsVisible => _textBox.IsVisible;
public bool IsEmpty() => string.IsNullOrEmpty(_textBox.Text);
public bool IsFocused() => _textBox.IsKeyboardFocused;
public event EventHandler ContentChanged;
public event EventHandler IsVisibleChanged;
public event EventHandler Loaded;
public event EventHandler FocusedChanged;
public TextBoxHintProxy(TextBox textBox)
{
if (textBox == null) throw new ArgumentNullException(nameof(textBox));
_textBox = textBox;
_textBox.TextChanged += TextBoxTextChanged;
_textBox.Loaded += TextBoxLoaded;
_textBox.IsVisibleChanged += TextBoxIsVisibleChanged;
_textBox.IsKeyboardFocusedChanged += TextBoxIsKeyboardFocusedChanged;
}
private void TextBoxIsKeyboardFocusedChanged(object sender, DependencyPropertyChangedEventArgs e)
{
FocusedChanged?.Invoke(sender, EventArgs.Empty);
}
private void TextBoxIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
IsVisibleChanged?.Invoke(sender, EventArgs.Empty);
}
private void TextBoxLoaded(object sender, RoutedEventArgs e)
{
Loaded?.Invoke(sender, EventArgs.Empty);
}
private void TextBoxTextChanged(object sender, TextChangedEventArgs e)
{
ContentChanged?.Invoke(sender, EventArgs.Empty);
}
public void Dispose()
{
_textBox.TextChanged -= TextBoxTextChanged;
_textBox.Loaded -= TextBoxLoaded;
_textBox.IsVisibleChanged -= TextBoxIsVisibleChanged;
_textBox.IsKeyboardFocusedChanged -= TextBoxIsKeyboardFocusedChanged;
}
}
}
}

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