summaryrefslogtreecommitdiff
path: root/zto/windows/WinUI
diff options
context:
space:
mode:
authorJoseph Henry <[email protected]>2017-03-14 13:21:54 -0700
committerJoseph Henry <[email protected]>2017-03-14 13:21:54 -0700
commit695b8ecc55a4b54aa47df181c1cdb674fd4b512c (patch)
tree8b7815a5c2f4094d72cff5552d73dc7340277e3c /zto/windows/WinUI
parente800e47a6333a0b4fe7df2a6093716cb2b44d308 (diff)
upgraded core to 1.2.00.8.0
Diffstat (limited to 'zto/windows/WinUI')
-rw-r--r--zto/windows/WinUI/APIHandler.cs346
-rw-r--r--zto/windows/WinUI/AboutView.xaml103
-rw-r--r--zto/windows/WinUI/AboutView.xaml.cs35
-rw-r--r--zto/windows/WinUI/App.config6
-rw-r--r--zto/windows/WinUI/App.xaml14
-rw-r--r--zto/windows/WinUI/App.xaml.cs25
-rw-r--r--zto/windows/WinUI/JoinNetworkView.xaml16
-rw-r--r--zto/windows/WinUI/JoinNetworkView.xaml.cs126
-rw-r--r--zto/windows/WinUI/MainWindow.xaml.cs233
-rw-r--r--zto/windows/WinUI/NetworkInfoView.xaml85
-rw-r--r--zto/windows/WinUI/NetworkInfoView.xaml.cs166
-rw-r--r--zto/windows/WinUI/NetworkListView.xaml88
-rw-r--r--zto/windows/WinUI/NetworkListView.xaml.cs85
-rw-r--r--zto/windows/WinUI/NetworkMonitor.cs202
-rw-r--r--zto/windows/WinUI/NetworkRoute.cs42
-rw-r--r--zto/windows/WinUI/NetworksPage.xaml13
-rw-r--r--zto/windows/WinUI/NetworksPage.xaml.cs99
-rw-r--r--zto/windows/WinUI/PeersPage.xaml26
-rw-r--r--zto/windows/WinUI/PeersPage.xaml.cs54
-rw-r--r--zto/windows/WinUI/PreferencesView.xaml13
-rw-r--r--zto/windows/WinUI/PreferencesView.xaml.cs50
-rw-r--r--zto/windows/WinUI/Properties/AssemblyInfo.cs56
-rw-r--r--zto/windows/WinUI/Properties/Resources.Designer.cs73
-rw-r--r--zto/windows/WinUI/Properties/Resources.resx124
-rw-r--r--zto/windows/WinUI/Properties/Settings.Designer.cs30
-rw-r--r--zto/windows/WinUI/Properties/Settings.settings7
-rw-r--r--zto/windows/WinUI/Resources/ZeroTierIcon.icobin0 -> 45451 bytes
-rw-r--r--zto/windows/WinUI/Simple Styles.xaml1128
-rw-r--r--zto/windows/WinUI/Themes/Generic.xaml7
-rw-r--r--zto/windows/WinUI/ToolbarItem.xaml59
-rw-r--r--zto/windows/WinUI/ToolbarItem.xaml.cs310
-rw-r--r--zto/windows/WinUI/WinUI.csproj265
-rw-r--r--zto/windows/WinUI/ZeroTierIcon.icobin0 -> 370070 bytes
-rw-r--r--zto/windows/WinUI/ZeroTierNetwork.cs494
-rw-r--r--zto/windows/WinUI/ZeroTierPeer.cs116
-rw-r--r--zto/windows/WinUI/ZeroTierPeerPhysicalPath.cs27
-rw-r--r--zto/windows/WinUI/ZeroTierStatus.cs39
-rw-r--r--zto/windows/WinUI/app.manifest55
-rw-r--r--zto/windows/WinUI/packages.config5
39 files changed, 4622 insertions, 0 deletions
diff --git a/zto/windows/WinUI/APIHandler.cs b/zto/windows/WinUI/APIHandler.cs
new file mode 100644
index 0000000..81c5b77
--- /dev/null
+++ b/zto/windows/WinUI/APIHandler.cs
@@ -0,0 +1,346 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Net;
+using System.IO;
+using System.Windows;
+using Newtonsoft.Json;
+using System.Diagnostics;
+
+namespace WinUI
+{
+
+
+ public class APIHandler
+ {
+ private string authtoken;
+
+ private string url = null;
+
+ private static volatile APIHandler instance;
+ private static object syncRoot = new Object();
+
+ public delegate void NetworkListCallback(List<ZeroTierNetwork> networks);
+ public delegate void StatusCallback(ZeroTierStatus status);
+
+ public static APIHandler Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ lock (syncRoot)
+ {
+ if (instance == null)
+ {
+ if (!initHandler())
+ {
+ return null;
+ }
+ }
+ }
+ }
+
+ return instance;
+ }
+ }
+
+ private static bool initHandler()
+ {
+ String localZtDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ String globalZtDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\ZeroTier\\One";
+
+ String authToken = "";
+ Int32 port = 9993;
+
+ if (!File.Exists(localZtDir + "\\authtoken.secret") || !File.Exists(localZtDir + "\\zerotier-one.port"))
+ {
+ // launch external process to copy file into place
+ String curPath = System.Reflection.Assembly.GetEntryAssembly().Location;
+ int index = curPath.LastIndexOf("\\");
+ curPath = curPath.Substring(0, index);
+ ProcessStartInfo startInfo = new ProcessStartInfo(curPath + "\\copyutil.exe", "\""+globalZtDir+"\"" + " " + "\""+localZtDir+"\"");
+ startInfo.Verb = "runas";
+
+
+ var process = Process.Start(startInfo);
+ process.WaitForExit();
+ }
+
+ authToken = readAuthToken(localZtDir + "\\authtoken.secret");
+
+ if ((authToken == null) || (authToken.Length <= 0))
+ {
+ MessageBox.Show("Unable to read ZeroTier One authtoken", "ZeroTier One");
+ return false;
+ }
+
+ port = readPort(localZtDir + "\\zerotier-one.port");
+ instance = new APIHandler(port, authToken);
+ return true;
+ }
+
+ private static String readAuthToken(String path)
+ {
+ String authToken = "";
+
+ if (File.Exists(path))
+ {
+ try
+ {
+ byte[] tmp = File.ReadAllBytes(path);
+ authToken = System.Text.Encoding.UTF8.GetString(tmp).Trim();
+ }
+ catch
+ {
+ MessageBox.Show("Unable to read ZeroTier One Auth Token from:\r\n" + path, "ZeroTier One");
+ }
+ }
+
+ return authToken;
+ }
+
+ private static Int32 readPort(String path)
+ {
+ Int32 port = 9993;
+
+ try
+ {
+ byte[] tmp = File.ReadAllBytes(path);
+ port = Int32.Parse(System.Text.Encoding.ASCII.GetString(tmp).Trim());
+ if ((port <= 0) || (port > 65535))
+ port = 9993;
+ }
+ catch
+ {
+ }
+
+ return port;
+ }
+
+ private APIHandler()
+ {
+ url = "http://127.0.0.1:9993";
+ }
+
+ public APIHandler(int port, string authtoken)
+ {
+ url = "http://localhost:" + port;
+ this.authtoken = authtoken;
+ }
+
+
+
+ public void GetStatus(StatusCallback cb)
+ {
+ var request = WebRequest.Create(url + "/status" + "?auth=" + authtoken) as HttpWebRequest;
+ if (request != null)
+ {
+ request.Method = "GET";
+ request.ContentType = "application/json";
+ }
+
+ try
+ {
+ var httpResponse = (HttpWebResponse)request.GetResponse();
+ using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
+ {
+ var responseText = streamReader.ReadToEnd();
+
+ ZeroTierStatus status = null;
+ try
+ {
+ status = JsonConvert.DeserializeObject<ZeroTierStatus>(responseText);
+ }
+ catch (JsonReaderException e)
+ {
+ Console.WriteLine(e.ToString());
+ }
+ cb(status);
+ }
+ }
+ catch (System.Net.Sockets.SocketException)
+ {
+ cb(null);
+ }
+ catch (System.Net.WebException)
+ {
+ cb(null);
+ }
+ }
+
+
+
+ public void GetNetworks(NetworkListCallback cb)
+ {
+ var request = WebRequest.Create(url + "/network" + "?auth=" + authtoken) as HttpWebRequest;
+ if (request == null)
+ {
+ cb(null);
+ }
+
+ request.Method = "GET";
+ request.ContentType = "application/json";
+ request.Timeout = 10000;
+
+ try
+ {
+ var httpResponse = (HttpWebResponse)request.GetResponse();
+ using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
+ {
+ var responseText = streamReader.ReadToEnd();
+
+ List<ZeroTierNetwork> networkList = null;
+ try
+ {
+ networkList = JsonConvert.DeserializeObject<List<ZeroTierNetwork>>(responseText);
+ foreach (ZeroTierNetwork n in networkList)
+ {
+ // all networks received via JSON are connected by definition
+ n.IsConnected = true;
+ }
+ }
+ catch (JsonReaderException e)
+ {
+ Console.WriteLine(e.ToString());
+ }
+ cb(networkList);
+ }
+ }
+ catch (System.Net.Sockets.SocketException)
+ {
+ cb(null);
+ }
+ catch (System.Net.WebException)
+ {
+ cb(null);
+ }
+ }
+
+ public void JoinNetwork(string nwid, bool allowManaged = true, bool allowGlobal = false, bool allowDefault = false)
+ {
+ var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
+ if (request == null)
+ {
+ return;
+ }
+
+ request.Method = "POST";
+ request.ContentType = "applicaiton/json";
+ request.Timeout = 10000;
+ try
+ {
+ using (var streamWriter = new StreamWriter(((HttpWebRequest)request).GetRequestStream()))
+ {
+ string json = "{\"allowManaged\":" + (allowManaged ? "true" : "false") + "," +
+ "\"allowGlobal\":" + (allowGlobal ? "true" : "false") + "," +
+ "\"allowDefault\":" + (allowDefault ? "true" : "false") + "}";
+ streamWriter.Write(json);
+ streamWriter.Flush();
+ streamWriter.Close();
+ }
+ }
+ catch (System.Net.WebException)
+ {
+ MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
+ return;
+ }
+
+ try
+ {
+ var httpResponse = (HttpWebResponse)request.GetResponse();
+
+ if (httpResponse.StatusCode != HttpStatusCode.OK)
+ {
+ Console.WriteLine("Error sending join network message");
+ }
+ }
+ catch (System.Net.Sockets.SocketException)
+ {
+ MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
+ }
+ catch (System.Net.WebException)
+ {
+ MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
+ }
+ }
+
+ public void LeaveNetwork(string nwid)
+ {
+ var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
+ if (request == null)
+ {
+ return;
+ }
+
+ request.Method = "DELETE";
+ request.Timeout = 10000;
+
+ try
+ {
+ var httpResponse = (HttpWebResponse)request.GetResponse();
+
+ if (httpResponse.StatusCode != HttpStatusCode.OK)
+ {
+ Console.WriteLine("Error sending leave network message");
+ }
+ }
+ catch (System.Net.Sockets.SocketException)
+ {
+ MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
+ }
+ catch (System.Net.WebException)
+ {
+ MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
+ }
+ catch
+ {
+ Console.WriteLine("Error leaving network: Unknown error");
+ }
+ }
+
+ public delegate void PeersCallback(List<ZeroTierPeer> peers);
+
+ public void GetPeers(PeersCallback cb)
+ {
+ var request = WebRequest.Create(url + "/peer" + "?auth=" + authtoken) as HttpWebRequest;
+ if (request == null)
+ {
+ cb(null);
+ }
+
+ request.Method = "GET";
+ request.ContentType = "application/json";
+
+ try
+ {
+ var httpResponse = (HttpWebResponse)request.GetResponse();
+ using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
+ {
+ var responseText = streamReader.ReadToEnd();
+ //Console.WriteLine(responseText);
+ List<ZeroTierPeer> peerList = null;
+ try
+ {
+ peerList = JsonConvert.DeserializeObject<List<ZeroTierPeer>>(responseText);
+ }
+ catch (JsonReaderException e)
+ {
+ Console.WriteLine(e.ToString());
+ }
+ cb(peerList);
+ }
+ }
+ catch (System.Net.Sockets.SocketException)
+ {
+ cb(null);
+ }
+ catch (System.Net.WebException)
+ {
+ cb(null);
+ }
+ }
+ }
+}
diff --git a/zto/windows/WinUI/AboutView.xaml b/zto/windows/WinUI/AboutView.xaml
new file mode 100644
index 0000000..5def46a
--- /dev/null
+++ b/zto/windows/WinUI/AboutView.xaml
@@ -0,0 +1,103 @@
+<Window x:Class="WinUI.AboutView"
+ 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:WinUI"
+ mc:Ignorable="d"
+ Title="AboutView" Height="460" Width="300" Icon="ZeroTierIcon.ico">
+ <Grid>
+ <Image x:Name="image" HorizontalAlignment="Center" Height="100" Margin="0,10,0,0" VerticalAlignment="Top" Width="100" Source="ZeroTierIcon.ico"/>
+ <RichTextBox x:Name="richTextBox" HorizontalAlignment="Left" Height="307" Margin="10,115,0,0" VerticalAlignment="Top" Width="275" IsReadOnly="True" IsDocumentEnabled="True" BorderThickness="0">
+ <RichTextBox.Resources>
+ <Style TargetType="Hyperlink">
+ <Setter Property="Cursor" Value="Hand" />
+ </Style>
+ </RichTextBox.Resources>
+ <FlowDocument>
+ <Paragraph>
+ <Span FontWeight="Bold" FontSize="18" FontFamily="HelveticaNeue">
+ <Run Text="Getting Started"/>
+ </Span>
+ <Span FontWeight="Bold" FontSize="12" FontFamily="HelveticaNeue">
+ <LineBreak/>
+ </Span>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run/>
+ </Span>
+ </Paragraph>
+ <Paragraph>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text="Getting started is simple. Simply click "/>
+ </Span>
+ <Span FontSize="12" FontFamily="Menlo-Regular">
+ <Run Text="Join Network"/>
+ </Span>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text=" from the ZeroTier status bar menu. To join the public network &quot;Earth&quot;, enter "/>
+ </Span>
+ <Span FontSize="12" FontFamily="Menlo-Regular">
+ <Run Text="8056c2e21c000001"/>
+ </Span>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text=" and click the Join button. Once connected, you'll be able to navigate to "/>
+ </Span>
+ <Hyperlink NavigateUri="http://earth.zerotier.net/" RequestNavigate="Hyperlink_MouseLeftButtonDown">
+ <Span Foreground="#FF0000E9" FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text="earth.zerotier.net"/>
+ </Span>
+ </Hyperlink>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text="."/>
+ </Span>
+ </Paragraph>
+ <Paragraph>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run/>
+ <LineBreak/>
+ </Span>
+ <Span FontWeight="Bold" FontSize="18" FontFamily="HelveticaNeue">
+ <Run Text="Create a Network"/>
+ </Span>
+ <Span FontWeight="Bold" FontSize="12" FontFamily="HelveticaNeue">
+ <LineBreak/>
+ </Span>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run/>
+ </Span>
+ </Paragraph>
+ <Paragraph>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text="Visit "/>
+ </Span>
+ <Hyperlink NavigateUri="http://my.zerotier.com/" RequestNavigate="Hyperlink_MouseLeftButtonDown">
+ <Span Foreground="#FF0000E9" FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text="my.zerotier.com"/>
+ </Span>
+ </Hyperlink>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text=" to create and manage your own virtual networks."/>
+ </Span>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <LineBreak/>
+ <Run/>
+ </Span>
+ </Paragraph>
+ <Paragraph>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text="For more information, visit "/>
+ </Span>
+ <Hyperlink NavigateUri="http://www.zerotier.com/" RequestNavigate="Hyperlink_MouseLeftButtonDown">
+ <Span Foreground="#FF0000E9" FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text="zerotier.com"/>
+ </Span>
+ </Hyperlink>
+ <Span FontSize="12" FontFamily="HelveticaNeue">
+ <Run Text="."/>
+ </Span>
+ </Paragraph>
+ </FlowDocument>
+ </RichTextBox>
+
+ </Grid>
+</Window>
diff --git a/zto/windows/WinUI/AboutView.xaml.cs b/zto/windows/WinUI/AboutView.xaml.cs
new file mode 100644
index 0000000..9c48493
--- /dev/null
+++ b/zto/windows/WinUI/AboutView.xaml.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+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.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for AboutView.xaml
+ /// </summary>
+ public partial class AboutView : Window
+ {
+ public AboutView()
+ {
+ InitializeComponent();
+ }
+
+ private void Hyperlink_MouseLeftButtonDown(object sender, RequestNavigateEventArgs e)
+ {
+ var hyperlink = (Hyperlink)sender;
+ Process.Start(hyperlink.NavigateUri.ToString());
+ }
+ }
+}
diff --git a/zto/windows/WinUI/App.config b/zto/windows/WinUI/App.config
new file mode 100644
index 0000000..8e15646
--- /dev/null
+++ b/zto/windows/WinUI/App.config
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+ </startup>
+</configuration> \ No newline at end of file
diff --git a/zto/windows/WinUI/App.xaml b/zto/windows/WinUI/App.xaml
new file mode 100644
index 0000000..12ed85f
--- /dev/null
+++ b/zto/windows/WinUI/App.xaml
@@ -0,0 +1,14 @@
+<Application x:Class="WinUI.App"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ StartupUri="ToolbarItem.xaml">
+ <Application.Resources>
+
+ <ResourceDictionary>
+ <ResourceDictionary.MergedDictionaries>
+ <ResourceDictionary Source="Simple Styles.xaml"/>
+ </ResourceDictionary.MergedDictionaries>
+ </ResourceDictionary>
+
+ </Application.Resources>
+</Application>
diff --git a/zto/windows/WinUI/App.xaml.cs b/zto/windows/WinUI/App.xaml.cs
new file mode 100644
index 0000000..53ef2f6
--- /dev/null
+++ b/zto/windows/WinUI/App.xaml.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+using Hardcodet.Wpf.TaskbarNotification;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for App.xaml
+ /// </summary>
+ public partial class App : Application
+ {
+ private TaskbarIcon tb;
+
+ private void InitApplication()
+ {
+ tb = (TaskbarIcon)FindResource("NotifyIcon");
+ tb.Visibility = Visibility.Visible;
+ }
+ }
+}
diff --git a/zto/windows/WinUI/JoinNetworkView.xaml b/zto/windows/WinUI/JoinNetworkView.xaml
new file mode 100644
index 0000000..1cd1e98
--- /dev/null
+++ b/zto/windows/WinUI/JoinNetworkView.xaml
@@ -0,0 +1,16 @@
+<Window x:Class="WinUI.JoinNetworkView"
+ 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:WinUI"
+ mc:Ignorable="d"
+ Title="Join a Network" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto" Icon="ZeroTierIcon.ico">
+ <Grid HorizontalAlignment="Left" Margin="0,0,0,0" Width="315">
+ <TextBox x:Name="joinNetworkBox" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="291" PreviewTextInput="joinNetworkBox_OnTextEntered" PreviewKeyDown="joinNetworkBox_OnKeyDown"/>
+ <CheckBox x:Name="allowManagedCheckbox" Content="Allow Managed" HorizontalAlignment="Left" Margin="10,38,0,0" VerticalAlignment="Top" IsChecked="True"/>
+ <CheckBox x:Name="allowGlobalCheckbox" Content="Allow Global" HorizontalAlignment="Left" Margin="118,38,0,0" VerticalAlignment="Top"/>
+ <CheckBox x:Name="allowDefaultCheckbox" Content="Allow Default" HorizontalAlignment="Left" Margin="210,38,-6,0" VerticalAlignment="Top"/>
+ <Button x:Name="joinButton" Content="Join" HorizontalAlignment="Left" Margin="226,58,0,10" Background="#FFFFB354" VerticalAlignment="Top" Width="75" Click="joinButton_Click" IsEnabled="False"/>
+ </Grid>
+</Window>
diff --git a/zto/windows/WinUI/JoinNetworkView.xaml.cs b/zto/windows/WinUI/JoinNetworkView.xaml.cs
new file mode 100644
index 0000000..548a51e
--- /dev/null
+++ b/zto/windows/WinUI/JoinNetworkView.xaml.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+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;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for JoinNetworkView.xaml
+ /// </summary>
+ public partial class JoinNetworkView : Window
+ {
+ Regex charRegex = new Regex("[0-9a-fxA-FX]");
+ Regex wholeStringRegex = new Regex("^[0-9a-fxA-FX]+$");
+
+ public JoinNetworkView()
+ {
+ InitializeComponent();
+
+ DataObject.AddPastingHandler(joinNetworkBox, onPaste);
+ DataObject.AddCopyingHandler(joinNetworkBox, onCopyCut);
+ }
+
+ private void joinNetworkBox_OnTextEntered(object sender, TextCompositionEventArgs e)
+ {
+ e.Handled = !charRegex.IsMatch(e.Text);
+
+ if ( (joinNetworkBox.Text.Length + e.Text.Length) == 16)
+ {
+ joinButton.IsEnabled = true;
+ }
+ else
+ {
+ joinButton.IsEnabled = false;
+ }
+ }
+
+ private void joinNetworkBox_OnKeyDown(object sender, KeyEventArgs e)
+ {
+ if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
+ {
+ if (e.Key == Key.X && joinNetworkBox.IsSelectionActive)
+ {
+ // handle ctrl-x removing characters
+ joinButton.IsEnabled = false;
+ }
+ }
+ else if (e.Key == Key.Delete || e.Key == Key.Back)
+ {
+ if ((joinNetworkBox.Text.Length - 1) == 16)
+ {
+ joinButton.IsEnabled = true;
+ }
+ else
+ {
+ joinButton.IsEnabled = false;
+ }
+ }
+ else
+ {
+ if ((joinNetworkBox.Text.Length + 1) > 16)
+ {
+ e.Handled = true;
+ }
+ }
+ }
+
+ private void onPaste(object sender, DataObjectPastingEventArgs e)
+ {
+ var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
+ if (!isText)
+ {
+ joinButton.IsEnabled = false;
+ return;
+ }
+
+ var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
+
+ if (!wholeStringRegex.IsMatch(text))
+ {
+ e.Handled = true;
+ e.CancelCommand();
+ }
+
+ if (text.Length == 16 || (joinNetworkBox.Text.Length + text.Length) == 16)
+ {
+ joinButton.IsEnabled = true;
+ }
+ else if (text.Length > 16 || (joinNetworkBox.Text.Length + text.Length) > 16)
+ {
+ e.Handled = true;
+ e.CancelCommand();
+ }
+ else
+ {
+ joinButton.IsEnabled = false;
+ }
+ }
+
+ private void onCopyCut(object sender, DataObjectCopyingEventArgs e)
+ {
+
+ }
+
+ private void joinButton_Click(object sender, RoutedEventArgs e)
+ {
+ bool allowDefault = allowDefaultCheckbox.IsChecked.Value;
+ bool allowGlobal = allowGlobalCheckbox.IsChecked.Value;
+ bool allowManaged = allowManagedCheckbox.IsChecked.Value;
+
+ APIHandler.Instance.JoinNetwork(joinNetworkBox.Text, allowManaged, allowGlobal, allowDefault);
+
+ Close();
+ }
+ }
+}
diff --git a/zto/windows/WinUI/MainWindow.xaml.cs b/zto/windows/WinUI/MainWindow.xaml.cs
new file mode 100644
index 0000000..e4a82f9
--- /dev/null
+++ b/zto/windows/WinUI/MainWindow.xaml.cs
@@ -0,0 +1,233 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Timers;
+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.Navigation;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for MainWindow.xaml
+ /// </summary>
+ public partial class MainWindow : Window
+ {
+ APIHandler handler;
+ Regex charRegex = new Regex("[0-9a-fxA-FX]");
+ Regex wholeStringRegex = new Regex("^[0-9a-fxA-FX]+$");
+
+ Timer timer = new Timer();
+
+ bool connected = false;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ if (InitAPIHandler())
+ {
+ networksPage.SetAPIHandler(handler);
+
+ updateStatus();
+ if (!connected)
+ {
+ MessageBox.Show("Unable to connect to ZeroTier Service.");
+ }
+
+ updateNetworks();
+ //updatePeers();
+
+ DataObject.AddPastingHandler(joinNetworkID, OnPaste);
+
+ timer.Elapsed += new ElapsedEventHandler(OnUpdateTimer);
+ timer.Interval = 2000;
+ timer.Enabled = true;
+ }
+ }
+
+
+ private String readAuthToken(String path)
+ {
+ String authToken = "";
+
+ if (File.Exists(path))
+ {
+ try
+ {
+ byte[] tmp = File.ReadAllBytes(path);
+ authToken = System.Text.Encoding.UTF8.GetString(tmp).Trim();
+ }
+ catch
+ {
+ MessageBox.Show("Unable to read ZeroTier One Auth Token from:\r\n" + path, "ZeroTier One");
+ }
+ }
+
+ return authToken;
+ }
+
+ private Int32 readPort(String path)
+ {
+ Int32 port = 9993;
+
+ try
+ {
+ byte[] tmp = File.ReadAllBytes(path);
+ port = Int32.Parse(System.Text.Encoding.ASCII.GetString(tmp).Trim());
+ if ((port <= 0) || (port > 65535))
+ port = 9993;
+ }
+ catch
+ {
+ }
+
+ return port;
+ }
+
+ private bool InitAPIHandler()
+ {
+ String localZtDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ String globalZtDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\ZeroTier\\One";
+
+ String authToken = "";
+ Int32 port = 9993;
+
+ if (!File.Exists(localZtDir + "\\authtoken.secret") || !File.Exists(localZtDir + "\\zerotier-one.port"))
+ {
+ // launch external process to copy file into place
+ String curPath = System.Reflection.Assembly.GetEntryAssembly().Location;
+ int index = curPath.LastIndexOf("\\");
+ curPath = curPath.Substring(0, index);
+ ProcessStartInfo startInfo = new ProcessStartInfo(curPath + "\\copyutil.exe", globalZtDir + " " + localZtDir);
+ startInfo.Verb = "runas";
+
+
+ var process = Process.Start(startInfo);
+ process.WaitForExit();
+ }
+
+ authToken = readAuthToken(localZtDir + "\\authtoken.secret");
+
+ if ((authToken == null) || (authToken.Length <= 0))
+ {
+ MessageBox.Show("Unable to read ZeroTier One authtoken", "ZeroTier One");
+ this.Close();
+ return false;
+ }
+
+ port = readPort(localZtDir + "\\zerotier-one.port");
+ handler = new APIHandler(port, authToken);
+ return true;
+ }
+
+ private void updateStatus()
+ {
+ var status = handler.GetStatus();
+
+ if (status != null)
+ {
+ connected = true;
+
+ networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ this.networkId.Text = status.Address;
+ }));
+ versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ this.versionString.Content = status.Version;
+ }));
+ onlineStatus.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ this.onlineStatus.Content = (status.Online ? "ONLINE" : "OFFLINE");
+ }));
+ }
+ else
+ {
+ connected = false;
+
+ networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ this.networkId.Text = "";
+ }));
+ versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ this.versionString.Content = "0";
+ }));
+ onlineStatus.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ this.onlineStatus.Content = "OFFLINE";
+ }));
+ }
+ }
+
+ private void updateNetworks()
+ {
+ var networks = handler.GetNetworks();
+
+ networksPage.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ networksPage.setNetworks(networks);
+ }));
+ }
+
+ private void updatePeers()
+ {
+ //var peers = handler.GetPeers();
+
+ //peersPage.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ //{
+ // peersPage.SetPeers(peers);
+ //}));
+ }
+
+ private void OnUpdateTimer(object source, ElapsedEventArgs e)
+ {
+ updateStatus();
+ updateNetworks();
+ //updatePeers();
+ }
+
+ private void joinButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (joinNetworkID.Text.Length < 16)
+ {
+ MessageBox.Show("Invalid Network ID");
+ }
+ else
+ {
+ handler.JoinNetwork(joinNetworkID.Text);
+ }
+ }
+
+ private void OnNetworkEntered(object sender, TextCompositionEventArgs e)
+ {
+ e.Handled = !charRegex.IsMatch(e.Text);
+ }
+
+ private void OnPaste(object sender, DataObjectPastingEventArgs e)
+ {
+ var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
+ if (!isText) return;
+
+ var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
+
+ if (!wholeStringRegex.IsMatch(text))
+ {
+ e.CancelCommand();
+ }
+ }
+ }
+}
diff --git a/zto/windows/WinUI/NetworkInfoView.xaml b/zto/windows/WinUI/NetworkInfoView.xaml
new file mode 100644
index 0000000..6b32569
--- /dev/null
+++ b/zto/windows/WinUI/NetworkInfoView.xaml
@@ -0,0 +1,85 @@
+<UserControl Background="LightGray" x:Class="WinUI.NetworkInfoView"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ mc:Ignorable="d"
+ >
+
+ <Border Background="GhostWhite" BorderBrush="Gainsboro" BorderThickness="1" CornerRadius="8,8,8,8">
+ <Grid Background="GhostWhite" Margin="10,10,10,10">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="auto"/>
+ <ColumnDefinition Width="10"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ </Grid.RowDefinitions>
+
+ <Grid Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="auto"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+
+ <TextBox x:Name="networkId" Text="8056c2e21c000001" HorizontalAlignment="Left" Grid.Column="0" Foreground="#FF91A2A3" FontFamily="Lucida Console" BorderThickness="0" IsReadOnly="true" Background="Transparent"/>
+ <TextBox x:Name="networkName" Text="earth.zerotier.net" HorizontalAlignment="Right" Grid.Column="1" Foreground="#FF000000" BorderThickness="0" IsReadOnly="true" Background="Transparent"/>
+ </Grid>
+
+ <Separator Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3"/>
+
+ <TextBlock TextWrapping="Wrap" Text="Status" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="2" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Type" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="3" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="MAC" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="4" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="MTU" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="5" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Broadcast" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="6" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Bridging" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="7" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Device" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="8" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Managed IPs" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="9" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Allow Global IP" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="10" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Allow Managed IP" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="11" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Allow Default Route" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="12" Foreground="#FF000000"/>
+
+ <Rectangle Grid.Column="2" Grid.Row="2" Grid.RowSpan="11" Fill="#FFEEEEEE"/>
+
+ <TextBlock x:Name="networkStatus" FontFamily="Lucida Console" TextWrapping="Wrap" HorizontalAlignment="Right" Text="OK" TextAlignment="Right" Grid.Column="2" Grid.Row="2" Foreground="#FF000000"/>
+ <TextBlock x:Name="networkType" FontFamily="Lucida Console" TextWrapping="Wrap" Text="PUBLIC" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="3" Foreground="#FF000000"/>
+ <TextBlock x:Name="macAddress" FontFamily="Lucida Console" TextWrapping="Wrap" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="4" Foreground="#FF000000"><Span><Run Text="02:83:4a:1e:4b:3a"/></Span></TextBlock>
+ <TextBlock x:Name="mtu" FontFamily="Lucida Console" TextWrapping="Wrap" Text="2800" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="5" Foreground="#FF000000"/>
+ <TextBlock x:Name="broadcastEnabled" FontFamily="Lucida Console" TextWrapping="Wrap" Text="ENABLED" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="6" Foreground="#FF000000"/>
+ <TextBlock x:Name="bridgingEnabled" FontFamily="Lucida Console" TextWrapping="Wrap" Text="DISABLED" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="7" Background="#FFEEEEEE" Foreground="#FF000000"/>
+ <TextBlock x:Name="deviceName" FontFamily="Lucida Console" TextWrapping="Wrap" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="8" Foreground="#FF000000"><Span><Run Text="ethernet_32771"/></Span></TextBlock>
+ <TextBox x:Name="managedIps" TextWrapping="Wrap" FontFamily="Lucida Console" HorizontalAlignment="Right" TextAlignment="Right" Grid.Column="2" Grid.Row="9" Foreground="#FF000000" IsReadOnly="True" BorderThickness="0" Background="#FFEEEEEE" Text="28.2.169.248/7&#x0a;fd80:56c2:e21c:0000:0199:9383:4a02:a9f8/88"/>
+ <CheckBox x:Name="allowGlobal" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="10" />
+ <CheckBox x:Name="allowManaged" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="11" />
+ <CheckBox x:Name="allowDefault" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="12" />
+
+ <Separator Grid.Column="0" Grid.Row="13" Grid.ColumnSpan="3"/>
+
+ <Grid Grid.Column="0" Grid.Row="14" Grid.ColumnSpan="3" Background="GhostWhite">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="auto"/>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="auto"/>
+ </Grid.ColumnDefinitions>
+ <Button x:Name="deleteButton" Grid.Column="0" Content="Delete" HorizontalAlignment="Left" VerticalAlignment="Center" Width="75" Background="#FFFFB354" Click="deleteButton_Click"/>
+ <CheckBox x:Name="connectedCheckBox" Grid.Column="2" Content="Connected" HorizontalAlignment="Right" VerticalAlignment="Center" Checked="connectedCheckBox_Checked" Unchecked="connectedCheckbox_Unchecked"/>
+ </Grid>
+ </Grid>
+ </Border>
+</UserControl>
diff --git a/zto/windows/WinUI/NetworkInfoView.xaml.cs b/zto/windows/WinUI/NetworkInfoView.xaml.cs
new file mode 100644
index 0000000..1f99a1f
--- /dev/null
+++ b/zto/windows/WinUI/NetworkInfoView.xaml.cs
@@ -0,0 +1,166 @@
+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.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for NetworkInfoView.xaml
+ /// </summary>
+ public partial class NetworkInfoView : UserControl
+ {
+ public ZeroTierNetwork network;
+
+ public NetworkInfoView(ZeroTierNetwork network)
+ {
+ InitializeComponent();
+
+ this.network = network;
+
+ UpdateNetworkData();
+
+ allowDefault.Checked += AllowDefault_CheckStateChanged;
+ allowDefault.Unchecked += AllowDefault_CheckStateChanged;
+ allowGlobal.Checked += AllowGlobal_CheckStateChanged;
+ allowGlobal.Unchecked += AllowGlobal_CheckStateChanged;
+ allowManaged.Checked += AllowManaged_CheckStateChanged;
+ allowManaged.Unchecked += AllowManaged_CheckStateChanged;
+ }
+
+ private void UpdateNetworkData()
+ {
+
+ if (this.networkId.Text != network.NetworkId)
+ this.networkId.Text = network.NetworkId;
+
+ if (this.networkName.Text != network.NetworkName)
+ this.networkName.Text = network.NetworkName;
+
+ if (this.networkStatus.Text != network.NetworkStatus)
+ this.networkStatus.Text = network.NetworkStatus;
+
+ if (this.networkType.Text != network.NetworkType)
+ this.networkType.Text = network.NetworkType;
+
+ if (this.macAddress.Text != network.MacAddress)
+ this.macAddress.Text = network.MacAddress;
+
+ if (this.mtu.Text != network.MTU.ToString())
+ this.mtu.Text = network.MTU.ToString();
+
+ this.broadcastEnabled.Text = (network.BroadcastEnabled ? "ENABLED" : "DISABLED");
+ this.bridgingEnabled.Text = (network.Bridge ? "ENABLED" : "DISABLED");
+
+ if (this.deviceName.Text != network.DeviceName)
+ this.deviceName.Text = network.DeviceName;
+
+ string iplist = "";
+ for (int i = 0; i < network.AssignedAddresses.Length; ++i)
+ {
+ iplist += network.AssignedAddresses[i];
+ if (i < (network.AssignedAddresses.Length - 1))
+ iplist += "\n";
+ }
+
+ if (this.managedIps.Text != iplist)
+ this.managedIps.Text = iplist;
+
+ this.allowDefault.IsChecked = network.AllowDefault;
+ this.allowGlobal.IsChecked = network.AllowGlobal;
+ this.allowManaged.IsChecked = network.AllowManaged;
+
+ this.connectedCheckBox.Checked -= connectedCheckBox_Checked;
+ this.connectedCheckBox.Unchecked -= connectedCheckbox_Unchecked;
+
+ this.connectedCheckBox.IsChecked = network.IsConnected;
+
+ this.connectedCheckBox.Checked += connectedCheckBox_Checked;
+ this.connectedCheckBox.Unchecked += connectedCheckbox_Unchecked;
+ }
+
+ public bool HasNetwork(ZeroTierNetwork network)
+ {
+ if (this.network.NetworkId.Equals(network.NetworkId))
+ return true;
+
+ return false;
+ }
+
+ public void SetNetworkInfo(ZeroTierNetwork network)
+ {
+ this.network = network;
+
+ UpdateNetworkData();
+ }
+
+ private void deleteButton_Click(object sender, RoutedEventArgs e)
+ {
+ APIHandler.Instance.LeaveNetwork(network.NetworkId);
+ NetworkMonitor.Instance.RemoveNetwork(network.NetworkId);
+ }
+
+ private void AllowManaged_CheckStateChanged(object sender, RoutedEventArgs e)
+ {
+ CheckBox cb = sender as CheckBox;
+ APIHandler.Instance.JoinNetwork(network.NetworkId,
+ allowManaged.IsChecked ?? false,
+ allowGlobal.IsChecked ?? false,
+ allowDefault.IsChecked ?? false);
+ }
+
+ private void AllowGlobal_CheckStateChanged(object sender, RoutedEventArgs e)
+ {
+ CheckBox cb = sender as CheckBox;
+ APIHandler.Instance.JoinNetwork(network.NetworkId,
+ allowManaged.IsChecked ?? false,
+ allowGlobal.IsChecked ?? false,
+ allowDefault.IsChecked ?? false);
+ }
+
+ private void AllowDefault_CheckStateChanged(object sender, RoutedEventArgs e)
+ {
+ CheckBox cb = sender as CheckBox;
+ APIHandler.Instance.JoinNetwork(network.NetworkId,
+ allowManaged.IsChecked ?? false,
+ allowGlobal.IsChecked ?? false,
+ allowDefault.IsChecked ?? false);
+ }
+
+ private void connectedCheckBox_Checked(object sender, RoutedEventArgs e)
+ {
+ onConnectedCheckboxUpdated(true);
+ }
+
+ private void connectedCheckbox_Unchecked(object sender, RoutedEventArgs e)
+ {
+ onConnectedCheckboxUpdated(false);
+ }
+
+ private void onConnectedCheckboxUpdated(bool isChecked)
+ {
+ if (isChecked)
+ {
+ bool global = allowGlobal.IsChecked.Value;
+ bool managed = allowManaged.IsChecked.Value;
+ bool defRoute = allowDefault.IsChecked.Value;
+
+ APIHandler.Instance.JoinNetwork(networkId.Text, managed, global, defRoute);
+ }
+ else
+ {
+ APIHandler.Instance.LeaveNetwork(networkId.Text);
+ }
+ }
+ }
+}
diff --git a/zto/windows/WinUI/NetworkListView.xaml b/zto/windows/WinUI/NetworkListView.xaml
new file mode 100644
index 0000000..1dc774b
--- /dev/null
+++ b/zto/windows/WinUI/NetworkListView.xaml
@@ -0,0 +1,88 @@
+<Window
+ 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:WinUI"
+ mc:Ignorable="d" x:Class="WinUI.NetworkListView"
+ Title="ZeroTier One" SizeToContent="Width" Height="500" Width="Auto" Icon="ZeroTierIcon.ico">
+
+ <Window.Resources>
+ <SolidColorBrush x:Key="GreenBrush" Color="#ff91a2a3"/>
+
+ <SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
+
+ <SolidColorBrush x:Key="GreenDisabledBrush" Color="#FF234447" />
+
+ <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
+
+ <SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
+
+ <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
+
+ <Style TargetType="{x:Type DataGrid}">
+ <Setter Property="Background" Value="#FFF" />
+ <Setter Property="AlternationCount" Value="2" />
+ </Style>
+
+ <Style TargetType="{x:Type DataGridRow}">
+ <Style.Triggers>
+ <Trigger Property="ItemsControl.AlternationIndex" Value="0">
+ <Setter Property="Background" Value="#EEE"></Setter>
+ </Trigger>
+ <Trigger Property="ItemsControl.AlternationIndex" Value="1">
+ <Setter Property="Background" Value="#FFF"></Setter>
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+
+ <Style TargetType="{x:Type TabItem}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TabItem}">
+ <Grid>
+ <Border
+ Name="Border"
+ Margin="0,0,-4,0"
+ Background="{StaticResource GreenBrush}"
+ BorderBrush="{StaticResource SolidBorderBrush}"
+ BorderThickness="1,1,1,1"
+ CornerRadius="2,12,0,0" >
+ <ContentPresenter x:Name="ContentSite"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center"
+ ContentSource="Header"
+ Margin="12,2,12,2"
+ RecognizesAccessKey="True"/>
+ </Border>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+ <Setter Property="Panel.ZIndex" Value="100" />
+ <Setter TargetName="Border" Property="Background" Value="{StaticResource GreenDisabledBrush}" />
+ <Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
+ <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
+ <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ </Window.Resources>
+
+ <DockPanel>
+ <Grid Background="LightGray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*"/>
+ </Grid.RowDefinitions>
+ <local:NetworksPage x:Name="networksPage" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Grid.Row="0" Margin="0,0,0,0"/>
+ </Grid>
+ </DockPanel>
+</Window>
diff --git a/zto/windows/WinUI/NetworkListView.xaml.cs b/zto/windows/WinUI/NetworkListView.xaml.cs
new file mode 100644
index 0000000..26c4025
--- /dev/null
+++ b/zto/windows/WinUI/NetworkListView.xaml.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Timers;
+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.Navigation;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+using System.ComponentModel;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for MainWindow.xaml
+ /// </summary>
+ public partial class NetworkListView : Window
+ {
+ Regex charRegex = new Regex("[0-9a-fxA-FX]");
+ Regex wholeStringRegex = new Regex("^[0-9a-fxA-FX]+$");
+
+ public NetworkListView()
+ {
+ InitializeComponent();
+
+ Closed += onClosed;
+
+ NetworkMonitor.Instance.SubscribeNetworkUpdates(updateNetworks);
+ }
+
+ ~NetworkListView()
+ {
+ }
+
+ protected override void OnClosing(CancelEventArgs e)
+ {
+ e.Cancel = true;
+ Hide();
+ }
+
+ private void onClosed(object sender, System.EventArgs e)
+ {
+ NetworkMonitor.Instance.UnsubscribeNetworkUpdates(updateNetworks);
+ }
+
+ private void updateNetworks(List<ZeroTierNetwork> networks)
+ {
+ if (networks != null)
+ {
+ networksPage.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ networksPage.setNetworks(networks);
+ }));
+ }
+ }
+
+ private void OnNetworkEntered(object sender, TextCompositionEventArgs e)
+ {
+ e.Handled = !charRegex.IsMatch(e.Text);
+ }
+
+ private void OnPaste(object sender, DataObjectPastingEventArgs e)
+ {
+ var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
+ if (!isText) return;
+
+ var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
+
+ if (!wholeStringRegex.IsMatch(text))
+ {
+ e.CancelCommand();
+ }
+ }
+ }
+}
diff --git a/zto/windows/WinUI/NetworkMonitor.cs b/zto/windows/WinUI/NetworkMonitor.cs
new file mode 100644
index 0000000..c276079
--- /dev/null
+++ b/zto/windows/WinUI/NetworkMonitor.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace WinUI
+{
+ class NetworkMonitor
+ {
+ public delegate void NetworkListCallback(List<ZeroTierNetwork> networks);
+ public delegate void StatusCallback(ZeroTierStatus status);
+
+ private Thread runThread;
+ private NetworkListCallback _nwCb;
+ private StatusCallback _stCb;
+
+
+ private List<ZeroTierNetwork> _knownNetworks = new List<ZeroTierNetwork>();
+
+ private static NetworkMonitor instance;
+ private static object syncRoot = new object();
+
+ public static NetworkMonitor Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ lock (syncRoot)
+ {
+ if (instance == null)
+ {
+ instance = new NetworkMonitor();
+ }
+ }
+ }
+
+ return instance;
+ }
+ }
+
+ private NetworkMonitor()
+ {
+ runThread = new Thread(new ThreadStart(run));
+ loadNetworks();
+
+ runThread.Start();
+ }
+
+ ~NetworkMonitor()
+ {
+ runThread.Interrupt();
+ }
+
+ private void loadNetworks()
+ {
+ String dataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ String dataFile = Path.Combine(dataPath, "networks.dat");
+
+ if (File.Exists(dataFile))
+ {
+ List<ZeroTierNetwork> netList;
+
+ using (Stream stream = File.Open(dataFile, FileMode.Open))
+ {
+ var bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
+ netList = (List<ZeroTierNetwork>)bformatter.Deserialize(stream);
+ stream.Close();
+ }
+
+ lock (_knownNetworks)
+ {
+ _knownNetworks = netList;
+ }
+ }
+ }
+
+ private void writeNetworks()
+ {
+ String dataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ String dataFile = Path.Combine(dataPath, "networks.dat");
+
+ if (!Directory.Exists(dataPath))
+ {
+ Directory.CreateDirectory(dataPath);
+ }
+
+ using (Stream stream = File.Open(dataFile, FileMode.OpenOrCreate))
+ {
+ lock (_knownNetworks)
+ {
+ var bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
+ bformatter.Serialize(stream, _knownNetworks);
+ stream.Flush();
+ stream.Close();
+ }
+ }
+ }
+
+ private void apiNetworkCallback(List<ZeroTierNetwork> networks)
+ {
+ if (networks == null)
+ {
+ return;
+ }
+
+ lock (_knownNetworks)
+ {
+ _knownNetworks = _knownNetworks.Union(networks, new NetworkEqualityComparer()).ToList();
+
+ foreach (ZeroTierNetwork n in _knownNetworks)
+ {
+ if (networks.Contains(n))
+ {
+ n.IsConnected = true;
+ }
+ else
+ {
+ n.IsConnected = false;
+ }
+ }
+
+ _knownNetworks.Sort();
+ _nwCb(_knownNetworks);
+ }
+
+ writeNetworks();
+ }
+
+ private void apiStatusCallback(ZeroTierStatus status)
+ {
+ _stCb(status);
+ }
+
+ private void run()
+ {
+ try
+ {
+ while (runThread.IsAlive)
+ {
+ APIHandler handler = APIHandler.Instance;
+
+ if (handler != null)
+ {
+ handler.GetNetworks(apiNetworkCallback);
+ handler.GetStatus(apiStatusCallback);
+ }
+
+ Thread.Sleep(2000);
+ }
+ }
+ catch
+ {
+ Console.WriteLine("Monitor Thread Ended");
+ }
+ }
+
+ public void SubscribeStatusUpdates(StatusCallback cb)
+ {
+ _stCb += cb;
+ }
+
+ public void UnsubscribeStatusUpdates(StatusCallback cb)
+ {
+ _stCb -= cb;
+ }
+
+ public void SubscribeNetworkUpdates(NetworkListCallback cb)
+ {
+ _nwCb += cb;
+ }
+
+ public void UnsubscribeNetworkUpdates(NetworkListCallback cb)
+ {
+ _nwCb -= cb;
+ }
+
+ public void RemoveNetwork(String networkID)
+ {
+ lock(_knownNetworks)
+ {
+ foreach (ZeroTierNetwork n in _knownNetworks)
+ {
+ if (n.NetworkId.Equals(networkID))
+ {
+ _knownNetworks.Remove(n);
+ writeNetworks();
+ break;
+ }
+ }
+ }
+ }
+
+ public void StopMonitor()
+ {
+ runThread.Abort();
+ }
+ }
+}
diff --git a/zto/windows/WinUI/NetworkRoute.cs b/zto/windows/WinUI/NetworkRoute.cs
new file mode 100644
index 0000000..ce26ef7
--- /dev/null
+++ b/zto/windows/WinUI/NetworkRoute.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ [Serializable]
+ public class NetworkRoute : ISerializable
+ {
+ protected NetworkRoute(SerializationInfo info, StreamingContext ctx)
+ {
+ Target = info.GetString("target");
+ Via = info.GetString("via");
+ Flags = info.GetInt32("flags");
+ Metric = info.GetInt32("metric");
+ }
+
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext ctx)
+ {
+ info.AddValue("target", Target);
+ info.AddValue("via", Via);
+ info.AddValue("flags", Flags);
+ info.AddValue("metric", Metric);
+ }
+
+ [JsonProperty("target")]
+ public string Target { get; set; }
+
+ [JsonProperty("via")]
+ public string Via { get; set; }
+
+ [JsonProperty("flags")]
+ public int Flags { get; set; }
+
+ [JsonProperty("metric")]
+ public int Metric { get; set; }
+ }
+}
diff --git a/zto/windows/WinUI/NetworksPage.xaml b/zto/windows/WinUI/NetworksPage.xaml
new file mode 100644
index 0000000..6f95bc0
--- /dev/null
+++ b/zto/windows/WinUI/NetworksPage.xaml
@@ -0,0 +1,13 @@
+<UserControl x:Class="WinUI.NetworksPage"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <ScrollViewer x:Name="MyScrollViewer" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
+ <UniformGrid x:Name="wrapPanel" Background="#FFDDDDDD" HorizontalAlignment="Stretch" VerticalAlignment="Top" Columns="1">
+
+ </UniformGrid>
+ </ScrollViewer>
+</UserControl>
diff --git a/zto/windows/WinUI/NetworksPage.xaml.cs b/zto/windows/WinUI/NetworksPage.xaml.cs
new file mode 100644
index 0000000..39a2fef
--- /dev/null
+++ b/zto/windows/WinUI/NetworksPage.xaml.cs
@@ -0,0 +1,99 @@
+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.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for NetworksPage.xaml
+ /// </summary>
+ public partial class NetworksPage : UserControl
+ {
+ public NetworksPage()
+ {
+ InitializeComponent();
+ }
+
+ public void setNetworks(List<ZeroTierNetwork> networks)
+ {
+ if (networks == null)
+ {
+ this.wrapPanel.Children.Clear();
+ return;
+ }
+
+ foreach (ZeroTierNetwork network in networks)
+ {
+ NetworkInfoView view = ChildWithNetwork(network);
+ if (view != null)
+ {
+ view.SetNetworkInfo(network);
+ }
+ else
+ {
+ wrapPanel.Children.Add(
+ new NetworkInfoView(
+ network));
+ }
+ }
+
+ // remove networks we're no longer joined to.
+ List<ZeroTierNetwork> tmpList = GetNetworksFromChildren();
+ foreach (ZeroTierNetwork n in networks)
+ {
+ if (tmpList.Contains(n))
+ {
+ tmpList.Remove(n);
+ }
+ }
+
+ foreach (ZeroTierNetwork n in tmpList)
+ {
+ NetworkInfoView view = ChildWithNetwork(n);
+ if (view != null)
+ {
+ wrapPanel.Children.Remove(view);
+ }
+ }
+ }
+
+ private NetworkInfoView ChildWithNetwork(ZeroTierNetwork network)
+ {
+ List<NetworkInfoView> list = wrapPanel.Children.OfType<NetworkInfoView>().ToList();
+
+ foreach (NetworkInfoView view in list)
+ {
+ if (view.HasNetwork(network))
+ {
+ return view;
+ }
+ }
+
+ return null;
+ }
+
+ private List<ZeroTierNetwork> GetNetworksFromChildren()
+ {
+ List<ZeroTierNetwork> networks = new List<ZeroTierNetwork>(wrapPanel.Children.Count);
+
+ List<NetworkInfoView> list = wrapPanel.Children.OfType<NetworkInfoView>().ToList();
+ foreach (NetworkInfoView n in list)
+ {
+ networks.Add(n.network);
+ }
+
+ return networks;
+ }
+ }
+}
diff --git a/zto/windows/WinUI/PeersPage.xaml b/zto/windows/WinUI/PeersPage.xaml
new file mode 100644
index 0000000..57b11ed
--- /dev/null
+++ b/zto/windows/WinUI/PeersPage.xaml
@@ -0,0 +1,26 @@
+<UserControl x:Class="WinUI.PeersPage"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300" Background="White" Foreground="Black">
+
+ <DataGrid x:Name="dataGrid" GridLinesVisibility="None" AutoGenerateColumns="False" CanUserResizeColumns="True" Margin="0,0,0,0" CanUserReorderColumns="False" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" CanUserSortColumns="False">
+ <DataGrid.CellStyle>
+ <Style TargetType="DataGridCell">
+ <Setter Property="BorderThickness" Value="0"/>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
+ </Style>
+ </DataGrid.CellStyle>
+ <DataGrid.Columns>
+ <DataGridTextColumn Header="Address" Binding="{Binding Address}"/>
+ <DataGridTextColumn Header="Version" Binding="{Binding VersionString}"/>
+ <DataGridTextColumn Header="Latency" Binding="{Binding Latency}"/>
+ <DataGridTextColumn Header="Data Paths" Binding="{Binding DataPaths}"/>
+ <DataGridTextColumn Header="Last Unicast" Binding="{Binding LastUnicastFrame}"/>
+ <DataGridTextColumn Header="Last Multicast" Binding="{Binding LastMulticastFrame}"/>
+ <DataGridTextColumn Width="*" Header="Role" Binding="{Binding Role}"/>
+ </DataGrid.Columns>
+ </DataGrid>
+</UserControl>
diff --git a/zto/windows/WinUI/PeersPage.xaml.cs b/zto/windows/WinUI/PeersPage.xaml.cs
new file mode 100644
index 0000000..fac22a4
--- /dev/null
+++ b/zto/windows/WinUI/PeersPage.xaml.cs
@@ -0,0 +1,54 @@
+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.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for PeersPage.xaml
+ /// </summary>
+ public partial class PeersPage : UserControl
+ {
+ private List<ZeroTierPeer> peersList = new List<ZeroTierPeer>();
+
+ public PeersPage()
+ {
+ InitializeComponent();
+
+ dataGrid.ItemsSource = peersList;
+ }
+
+ public void SetPeers(List<ZeroTierPeer> list)
+ {
+ if (list == null)
+ return;
+
+
+ foreach(ZeroTierPeer p in list)
+ {
+ ZeroTierPeer curPeer = peersList.Find(peer => peer.Equals(p));
+ if (curPeer == null)
+ {
+ peersList.Add(p);
+ }
+ else
+ {
+ curPeer.Update(p);
+ }
+ }
+
+ dataGrid.Items.Refresh();
+ }
+ }
+}
diff --git a/zto/windows/WinUI/PreferencesView.xaml b/zto/windows/WinUI/PreferencesView.xaml
new file mode 100644
index 0000000..ac61fff
--- /dev/null
+++ b/zto/windows/WinUI/PreferencesView.xaml
@@ -0,0 +1,13 @@
+<Window x:Class="WinUI.PreferencesView"
+ 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:WinUI"
+ mc:Ignorable="d"
+ Title="PreferencesView" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto" Icon="ZeroTierIcon.ico">
+ <Grid>
+ <CheckBox x:Name="startupCheckbox" Content="Launch ZeroTier On Startup" HorizontalAlignment="Left" Margin="10,10,10,10" VerticalAlignment="Top" Checked="startupCheckbox_Checked" Unchecked="startupCheckbox_Unchecked"/>
+
+ </Grid>
+</Window>
diff --git a/zto/windows/WinUI/PreferencesView.xaml.cs b/zto/windows/WinUI/PreferencesView.xaml.cs
new file mode 100644
index 0000000..c6733be
--- /dev/null
+++ b/zto/windows/WinUI/PreferencesView.xaml.cs
@@ -0,0 +1,50 @@
+using Microsoft.Win32;
+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;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for PreferencesView.xaml
+ /// </summary>
+ public partial class PreferencesView : Window
+ {
+ public static string AppName = "ZeroTier One";
+ private RegistryKey rk = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
+
+ public PreferencesView()
+ {
+ InitializeComponent();
+
+
+ string keyValue = rk.GetValue(AppName) as string;
+
+ if (keyValue != null && keyValue.Equals(System.Reflection.Assembly.GetExecutingAssembly().Location))
+ {
+ startupCheckbox.IsChecked = true;
+ }
+ }
+
+ private void startupCheckbox_Checked(object sender, RoutedEventArgs e)
+ {
+ rk.SetValue(AppName, System.Reflection.Assembly.GetExecutingAssembly().Location);
+ }
+
+ private void startupCheckbox_Unchecked(object sender, RoutedEventArgs e)
+ {
+ rk.DeleteValue(AppName);
+ }
+
+ }
+}
diff --git a/zto/windows/WinUI/Properties/AssemblyInfo.cs b/zto/windows/WinUI/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..9c7cd13
--- /dev/null
+++ b/zto/windows/WinUI/Properties/AssemblyInfo.cs
@@ -0,0 +1,56 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ZeroTier One")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("ZeroTier, Inc")]
+[assembly: AssemblyProduct("ZeroTier One")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
+//inside a <PropertyGroup>. For example, if you are using US english
+//in your source files, set the <UICulture> to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: NeutralResourcesLanguageAttribute("en-US")]
diff --git a/zto/windows/WinUI/Properties/Resources.Designer.cs b/zto/windows/WinUI/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..3e5c78a
--- /dev/null
+++ b/zto/windows/WinUI/Properties/Resources.Designer.cs
@@ -0,0 +1,73 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace WinUI.Properties {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [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 {
+
+ 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() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinUI.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
+ /// </summary>
+ internal static System.Drawing.Icon ZeroTierIcon {
+ get {
+ object obj = ResourceManager.GetObject("ZeroTierIcon", resourceCulture);
+ return ((System.Drawing.Icon)(obj));
+ }
+ }
+ }
+}
diff --git a/zto/windows/WinUI/Properties/Resources.resx b/zto/windows/WinUI/Properties/Resources.resx
new file mode 100644
index 0000000..0872fce
--- /dev/null
+++ b/zto/windows/WinUI/Properties/Resources.resx
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+ <data name="ZeroTierIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>..\ZeroTierIcon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/zto/windows/WinUI/Properties/Settings.Designer.cs b/zto/windows/WinUI/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..6812ccc
--- /dev/null
+++ b/zto/windows/WinUI/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace WinUI.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
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/zto/windows/WinUI/Properties/Settings.settings b/zto/windows/WinUI/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/zto/windows/WinUI/Properties/Settings.settings
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
+ <Profiles>
+ <Profile Name="(Default)" />
+ </Profiles>
+ <Settings />
+</SettingsFile> \ No newline at end of file
diff --git a/zto/windows/WinUI/Resources/ZeroTierIcon.ico b/zto/windows/WinUI/Resources/ZeroTierIcon.ico
new file mode 100644
index 0000000..5d06b9f
--- /dev/null
+++ b/zto/windows/WinUI/Resources/ZeroTierIcon.ico
Binary files differ
diff --git a/zto/windows/WinUI/Simple Styles.xaml b/zto/windows/WinUI/Simple Styles.xaml
new file mode 100644
index 0000000..a03348b
--- /dev/null
+++ b/zto/windows/WinUI/Simple Styles.xaml
@@ -0,0 +1,1128 @@
+<ResourceDictionary 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/interactivedesigner/2006"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d"
+ xmlns:tb="http://www.hardcodet.net/taskbar">
+
+ <!-- SimpleStyles.XAML defines a set of control styles which are simplified starting points for creating your own controls -->
+
+ <!-- Brushes : These are used to define the color for background, foreground, selection, enabled etc of all controls
+ If you want to change the color of a control you can just chnage the brush; if you want to add a new shape or change arrangement then also edit the template -->
+
+ <!-- NormalBrush is used as the Background for SimpleButton, SimpleRepeatButton -->
+ <LinearGradientBrush x:Key="NormalBrush" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#EEE" Offset="0.0"/>
+ <GradientStop Color="#CCC" Offset="1.0"/>
+ </LinearGradientBrush>
+ <LinearGradientBrush x:Key="NormalBorderBrush" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#CCC" Offset="0.0"/>
+ <GradientStop Color="#444" Offset="1.0"/>
+ </LinearGradientBrush>
+
+ <!-- LightBrush is used for content areas such as Menu, Tab Control background -->
+ <LinearGradientBrush x:Key="LightBrush" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#FFF" Offset="0.0"/>
+ <GradientStop Color="#EEE" Offset="1.0"/>
+ </LinearGradientBrush>
+
+ <!-- MouseOverBrush is used for MouseOver in Button, Radio Button, CheckBox -->
+ <LinearGradientBrush x:Key="MouseOverBrush" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#FFF" Offset="0.0"/>
+ <GradientStop Color="#AAA" Offset="1.0"/>
+ </LinearGradientBrush>
+
+ <!-- PressedBrush is used for Pressed in Button, Radio Button, CheckBox -->
+ <LinearGradientBrush x:Key="PressedBrush" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#BBB" Offset="0.0"/>
+ <GradientStop Color="#EEE" Offset="0.1"/>
+ <GradientStop Color="#EEE" Offset="0.9"/>
+ <GradientStop Color="#FFF" Offset="1.0"/>
+ </LinearGradientBrush>
+ <LinearGradientBrush x:Key="PressedBorderBrush" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#444" Offset="0.0"/>
+ <GradientStop Color="#888" Offset="1.0"/>
+ </LinearGradientBrush>
+
+ <!-- SelectedBackgroundBrush is used for the Selected item in ListBoxItem, ComboBoxItem-->
+ <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD"/>
+
+ <!-- Disabled Brushes are used for the Disabled look of each control -->
+ <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888"/>
+ <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE"/>
+ <SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA"/>
+
+ <!-- Used for background of ScrollViewer, TreeView, ListBox, Expander, TextBox, Tab Control -->
+ <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF"/>
+
+ <!-- DefaultedBorderBrush is used to show KeyBoardFocus -->
+ <LinearGradientBrush x:Key="DefaultedBorderBrush" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#777" Offset="0.0"/>
+ <GradientStop Color="#000" Offset="1.0"/>
+ </LinearGradientBrush>
+
+ <SolidColorBrush x:Key="SolidBorderBrush" Color="#888"/>
+ <SolidColorBrush x:Key="LightBorderBrush" Color="#AAA"/>
+ <SolidColorBrush x:Key="LightColorBrush" Color="#DDD"/>
+
+ <!-- Used for Checkmark, Radio button, TreeViewItem, Expander ToggleButton glyphs -->
+ <SolidColorBrush x:Key="GlyphBrush" Color="#444"/>
+
+
+ <!-- Style and Template pairs are used to define each control Part -->
+ <!-- The Style provides default values on the control; the Template gives the elements for each control -->
+
+ <!-- SimpleButtonFocusVisual is used to show keyboard focus around a SimpleButton control -->
+ <Style x:Key="SimpleButtonFocusVisual">
+ <Setter Property="Control.Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Border>
+ <Rectangle Margin="2" Stroke="#60000000" StrokeThickness="1" StrokeDashArray="1 2"/>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple Button - This control sets brushes on each state. Note that these brushes must be listed above since they are static resources -->
+ <Style x:Key="SimpleButton" TargetType="{x:Type Button}" BasedOn="{x:Null}">
+ <Setter Property="FocusVisualStyle" Value="{DynamicResource SimpleButtonFocusVisual}"/>
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Button}">
+
+ <!-- We use Grid as a root because it is easy to add more elements to customize the button -->
+ <Grid x:Name="Grid">
+ <Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"/>
+
+ <!-- Content Presenter is where the text content etc is placed by the control -->
+ <!-- The bindings are useful so that the control can be parameterized without editing the template -->
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
+ </Grid>
+
+ <!--Each state sets a brush on the Border in the template -->
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsKeyboardFocused" Value="true">
+ <Setter Property="BorderBrush" Value="{DynamicResource DefaultedBorderBrush}" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Background" Value="{DynamicResource MouseOverBrush}" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Setter Property="Background" Value="{DynamicResource PressedBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource PressedBorderBrush}" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="true"/>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style x:Key="RadioButtonFocusVisual">
+ <Setter Property="Control.Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Border>
+ <Rectangle Margin="15,0,0,0" Stroke="#60000000" StrokeThickness="1" StrokeDashArray="1 2"/>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style x:Key="CheckBoxFocusVisual">
+ <Setter Property="Control.Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Border>
+ <Rectangle Margin="15,0,0,0" Stroke="#60000000" StrokeThickness="1" StrokeDashArray="1 2"/>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple CheckBox -->
+ <Style x:Key="SimpleCheckBox" TargetType="{x:Type CheckBox}">
+ <Setter Property="SnapsToDevicePixels" Value="true"/>
+ <Setter Property="FocusVisualStyle" Value="{DynamicResource CheckBoxFocusVisual}"/>
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type CheckBox}">
+
+ <!-- BulletDecorator is used to provide baseline alignment between the checkmark and the Content -->
+ <BulletDecorator Background="Transparent">
+ <BulletDecorator.Bullet>
+ <Grid Width="13" Height="13">
+ <Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
+ <Path x:Name="CheckMark" Stroke="{DynamicResource GlyphBrush}" StrokeThickness="2" SnapsToDevicePixels="False" Data="M 0 0 L 13 13 M 0 13 L 13 0"/>
+ </Grid>
+ </BulletDecorator.Bullet>
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
+ </BulletDecorator>
+
+ <!-- This uses Visibility to hide and show the CheckMark on IsChecked -->
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsChecked" Value="false">
+ <Setter Property="Visibility" Value="Collapsed" TargetName="CheckMark"/>
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Background" Value="{DynamicResource MouseOverBrush}" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Setter Property="Background" Value="{DynamicResource PressedBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource PressedBorderBrush}" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple Radio Button -->
+ <Style x:Key="SimpleRadioButton" TargetType="{x:Type RadioButton}">
+ <Setter Property="SnapsToDevicePixels" Value="true"/>
+ <Setter Property="FocusVisualStyle" Value="{DynamicResource RadioButtonFocusVisual}"/>
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type RadioButton}">
+
+ <!-- BulletDecorator is used to provide baseline alignment between the checkmark and the Content -->
+ <BulletDecorator Background="Transparent">
+ <BulletDecorator.Bullet>
+ <Grid Width="13" Height="13">
+ <Ellipse x:Name="Ellipse_Border" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="1"/>
+ <Ellipse Margin="4" x:Name="CheckMark" Fill="{DynamicResource GlyphBrush}"/>
+ </Grid>
+ </BulletDecorator.Bullet>
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
+ </BulletDecorator>
+
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsChecked" Value="false">
+ <Setter Property="Visibility" Value="Collapsed" TargetName="CheckMark"/>
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Fill" Value="{DynamicResource MouseOverBrush}" TargetName="Ellipse_Border"/>
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Setter Property="Fill" Value="{DynamicResource PressedBrush}" TargetName="Ellipse_Border"/>
+ <Setter Property="Stroke" Value="{DynamicResource GlyphBrush}" TargetName="Ellipse_Border"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Fill" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Ellipse_Border"/>
+ <Setter Property="Stroke" Value="#40000000" TargetName="Ellipse_Border"/>
+ <Setter Property="Foreground" Value="#80000000"/>
+ </Trigger>
+
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple Repeat Button - This is used by Simple ScrollBar for the up and down buttons -->
+ <Style x:Key="SimpleRepeatButton" d:IsControlPart="True" TargetType="{x:Type RepeatButton}" BasedOn="{x:Null}">
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type RepeatButton}">
+ <Grid>
+ <Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
+ <ContentPresenter HorizontalAlignment="Center" x:Name="ContentPresenter" VerticalAlignment="Center" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsKeyboardFocused" Value="true">
+ <Setter Property="BorderBrush" Value="{DynamicResource DefaultedBorderBrush}" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Background" Value="{DynamicResource MouseOverBrush}" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Setter Property="Background" Value="{DynamicResource PressedBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource PressedBorderBrush}" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple Thumb - The Thumb is the draggable part of the Scrollbar -->
+ <Style x:Key="SimpleThumbStyle" d:IsControlPart="True" TargetType="{x:Type Thumb}" BasedOn="{x:Null}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Thumb}">
+ <Grid Margin="0,0,0,0" x:Name="Grid">
+ <Rectangle HorizontalAlignment="Stretch" x:Name="Rectangle" VerticalAlignment="Stretch" Width="Auto" Height="Auto" RadiusX="2" RadiusY="2" Fill="{DynamicResource NormalBrush}" Stroke="{DynamicResource NormalBorderBrush}"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsFocused" Value="True"/>
+ <Trigger Property="IsMouseOver" Value="True"/>
+ <Trigger Property="IsEnabled" Value="False"/>
+ <Trigger Property="IsDragging" Value="True"/>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple ScrollRepeatButton Style - This RepeatButton is used above and below the Thumb in the Scrollbar. They are set to transparent si that they do not show over the scrollbar -->
+ <Style x:Key="SimpleScrollRepeatButtonStyle" d:IsControlPart="True" TargetType="{x:Type RepeatButton}">
+ <Setter Property="Background" Value="Transparent"/>
+ <Setter Property="BorderBrush" Value="Transparent"/>
+ <Setter Property="IsTabStop" Value="false"/>
+ <Setter Property="Focusable" Value="false"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type RepeatButton}">
+ <Grid>
+ <Rectangle Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}"/>
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple ScrollBar This makes use of SimpleThumb, SimpleRepeatButton and SimpleScrollRepeatButton -->
+
+ <Style x:Key="SimpleScrollBar" TargetType="{x:Type ScrollBar}">
+ <Setter Property="Stylus.IsFlicksEnabled" Value="false"/>
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ScrollBar}">
+ <Grid x:Name="GridRoot" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Background="{TemplateBinding Background}">
+ <Grid.RowDefinitions>
+ <RowDefinition MaxHeight="18"/>
+ <RowDefinition Height="0.00001*"/>
+ <RowDefinition MaxHeight="18"/>
+ </Grid.RowDefinitions>
+
+ <RepeatButton x:Name="DecreaseRepeat" Style="{DynamicResource SimpleRepeatButton}" Command="ScrollBar.LineUpCommand">
+ <Grid>
+ <Path x:Name="DecreaseArrow" Stroke="{TemplateBinding Foreground}" StrokeThickness="1" Data="M 0 4 L 8 4 L 4 0 Z"/>
+ </Grid>
+ </RepeatButton>
+
+ <!-- Track is a special layout container which sizes the thumb and the repeat button which do jump scrolling either side of it -->
+ <Track Grid.Row="1" x:Name="PART_Track" Orientation="Vertical" IsDirectionReversed="true">
+ <Track.Thumb>
+ <Thumb Style="{DynamicResource SimpleThumbStyle}"/>
+ </Track.Thumb>
+ <Track.IncreaseRepeatButton>
+ <RepeatButton x:Name="PageUp" Style="{DynamicResource SimpleScrollRepeatButtonStyle}" Command="ScrollBar.PageDownCommand"/>
+ </Track.IncreaseRepeatButton>
+ <Track.DecreaseRepeatButton>
+ <RepeatButton x:Name="PageDown" Style="{DynamicResource SimpleScrollRepeatButtonStyle}" Command="ScrollBar.PageUpCommand"/>
+ </Track.DecreaseRepeatButton>
+ </Track>
+
+ <RepeatButton Grid.Row="2" x:Name="IncreaseRepeat" Style="{DynamicResource SimpleRepeatButton}" Command="ScrollBar.LineDownCommand">
+ <Grid>
+ <Path x:Name="IncreaseArrow" Stroke="{TemplateBinding Foreground}" StrokeThickness="1" Data="M 0 0 L 4 4 L 8 0 Z"/>
+ </Grid>
+ </RepeatButton>
+ </Grid>
+
+ <!-- This uses a single template for ScrollBar and rotate it to be Horizontal
+ It also changes the commands so that the it does Left and Right instead of Up and Down Commands -->
+ <ControlTemplate.Triggers>
+ <Trigger Property="Orientation" Value="Horizontal">
+
+ <!-- Rotate the ScrollBar from Vertical to Horizontal -->
+ <Setter Property="LayoutTransform" TargetName="GridRoot">
+ <Setter.Value>
+ <RotateTransform Angle="-90"/>
+ </Setter.Value>
+ </Setter>
+
+ <!-- Track is bound to Orientation internally, so we need to rotate it back to Vertical -->
+ <Setter TargetName="PART_Track" Property="Orientation" Value="Vertical"/>
+
+ <!-- Change the commands to do Horizontal commands -->
+ <Setter Property="Command" Value="ScrollBar.LineLeftCommand" TargetName="DecreaseRepeat"/>
+ <Setter Property="Command" Value="ScrollBar.LineRightCommand" TargetName="IncreaseRepeat"/>
+ <Setter Property="Command" Value="ScrollBar.PageLeftCommand" TargetName="PageDown"/>
+ <Setter Property="Command" Value="ScrollBar.PageRightCommand" TargetName="PageUp"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple ScrollViewer
+ ScrollViewer is a Grid control which has a ContentPresenter and a Horizontal and Vertical ScrollBar
+ It is used by ListBox, MenuItem, ComboBox, and TreeView -->
+ <Style x:Key="SimpleScrollViewer" TargetType="{x:Type ScrollViewer}" BasedOn="{x:Null}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ScrollViewer}">
+ <Grid Background="{TemplateBinding Background}">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <ScrollContentPresenter Grid.Column="0" Grid.Row="0" Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}"/>
+
+ <!-- The visibility of the ScrollBars is controlled by the implementation fo the control -->
+ <ScrollBar Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Grid.Column="0" Grid.Row="1" x:Name="PART_HorizontalScrollBar" Style="{DynamicResource SimpleScrollBar}" Orientation="Horizontal" Value="{Binding Path=HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Minimum="0" Maximum="{TemplateBinding ScrollableWidth}" />
+ <ScrollBar Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Grid.Column="1" Grid.Row="0" x:Name="PART_VerticalScrollBar" Style="{DynamicResource SimpleScrollBar}" Orientation="Vertical" Value="{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" Minimum="0" Maximum="{TemplateBinding ScrollableHeight}" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple ListBox - This uses SimpleScrollViewer to allow items to be scrolled and SimpleListBoxItem to define the look of each item -->
+ <Style x:Key="SimpleListBox" TargetType="{x:Type ListBox}">
+ <Setter Property="SnapsToDevicePixels" Value="true"/>
+ <Setter Property="Background" Value="{DynamicResource WindowBackgroundBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource SolidBorderBrush}"/>
+ <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
+ <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
+ <Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ListBox}">
+ <Grid>
+ <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
+ <ScrollViewer Margin="1" Style="{DynamicResource SimpleScrollViewer}" Focusable="false" Background="{TemplateBinding Background}">
+
+ <!-- The StackPanel is used to display the children by setting IsItemsHost to be Trus -->
+ <StackPanel Margin="2" IsItemsHost="true"/>
+
+ </ScrollViewer>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsGrouping" Value="true">
+ <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple ListBoxItem - This is used for each Item in a ListBox. The item's content is placed in the ContentPresenter -->
+
+ <Style x:Key="SimpleListBoxItem" d:IsControlPart="True" TargetType="{x:Type ListBoxItem}">
+ <Setter Property="SnapsToDevicePixels" Value="true"/>
+ <Setter Property="OverridesDefaultStyle" Value="true"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ListBoxItem}">
+ <Grid SnapsToDevicePixels="true">
+ <Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+
+ <!-- Change IsSelected SelectedBackgroundBrush to set the selection color for the items -->
+ <Trigger Property="IsSelected" Value="true">
+ <Setter Property="Background" Value="{DynamicResource SelectedBackgroundBrush}" TargetName="Border"/>
+ </Trigger>
+
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple Expander ToggleButton - This Button is used by the Expander control. When it is toggled it switches visibility on the Up_Arrow and Down_Arrow -->
+ <ControlTemplate x:Key="ExpanderToggleButton" TargetType="{x:Type ToggleButton}">
+ <Grid>
+ <Rectangle Margin="0,0,0,0" x:Name="Rectangle" Fill="Transparent" Stroke="{DynamicResource NormalBorderBrush}"/>
+ <Path HorizontalAlignment="Center" x:Name="Up_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z"/>
+ <Path Visibility="Collapsed" HorizontalAlignment="Center" x:Name="Down_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 4 L 4 0 L 8 4 Z"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Fill" Value="{DynamicResource MouseOverBrush}" TargetName="Rectangle"/>
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Setter Property="Fill" Value="{DynamicResource PressedBrush}" TargetName="Rectangle"/>
+ </Trigger>
+ <Trigger Property="IsChecked" Value="true">
+ <Setter Property="Visibility" Value="Visible" TargetName="Down_Arrow"/>
+ <Setter Property="Visibility" Value="Collapsed" TargetName="Up_Arrow"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Fill" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Rectangle"/>
+ <Setter Property="Stroke" Value="{DynamicResource DisabledBorderBrush}" TargetName="Rectangle"/>
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ <Setter Property="Fill" Value="{DynamicResource DisabledForegroundBrush}" TargetName="Up_Arrow"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+
+ <!-- Simple Expander
+ This uses the Simpler ExpanderToggleButton. It sets Visibility on the ContentPresenter to expand
+ Limitations : The Simple Expander only expands down -->
+ <Style x:Key="SimpleExpander" TargetType="{x:Type Expander}">
+ <Setter Property="Background" Value="{DynamicResource LightBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Expander}">
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*" x:Name="ContentRow"/>
+ </Grid.RowDefinitions>
+ <Border Grid.Row="0" x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,0,0">
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="20"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ <ToggleButton Template="{DynamicResource ExpanderToggleButton}" Background="{DynamicResource NormalBrush}" IsChecked="{Binding Path=IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" OverridesDefaultStyle="True"/>
+ <ContentPresenter Grid.Column="1" Margin="4" RecognizesAccessKey="True" ContentSource="Header"/>
+ </Grid>
+ </Border>
+ <Border Visibility="Collapsed" Grid.Row="1" x:Name="ExpandSite" Background="{DynamicResource WindowBackgroundBrush}" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1,0,1,1" CornerRadius="0,0,2,2">
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Focusable="false"/>
+ </Border>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsExpanded" Value="True">
+ <Setter Property="Visibility" Value="Visible" TargetName="ExpandSite"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple ComboBox Toggle Button - This is used in ComboBox to expand and collapse the ComboBox Popup-->
+ <ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition/>
+ <ColumnDefinition Width="20"/>
+ </Grid.ColumnDefinitions>
+ <Rectangle Grid.ColumnSpan="2" HorizontalAlignment="Stretch" x:Name="Rectangle" VerticalAlignment="Stretch" Width="Auto" Height="Auto" RadiusX="5" RadiusY="5" Fill="{DynamicResource NormalBrush}" Stroke="{DynamicResource NormalBorderBrush}"/>
+ <Rectangle Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" RadiusX="5" RadiusY="5" Fill="{DynamicResource WindowBackgroundBrush}" Stroke="{DynamicResource NormalBorderBrush}"/>
+ <Path Grid.Column="1" HorizontalAlignment="Center" x:Name="Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Fill" Value="{DynamicResource MouseOverBrush}" TargetName="Rectangle"/>
+ </Trigger>
+ <Trigger Property="IsChecked" Value="true">
+ <Setter Property="Fill" Value="{DynamicResource PressedBrush}" TargetName="Rectangle"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Fill" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Rectangle"/>
+ <Setter Property="Stroke" Value="{DynamicResource DisabledBorderBrush}" TargetName="Rectangle"/>
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ <Setter Property="Fill" Value="{DynamicResource DisabledForegroundBrush}" TargetName="Arrow"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+
+ <!-- This is the area which contains the selected item in the ComboBox -->
+
+ <ControlTemplate x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}">
+ <!-- This must be named as PART_ContentHost -->
+ <Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}"/>
+ </ControlTemplate>
+
+ <!-- Simple ComboBox
+ This uses the ComboBoxToggleButton to expand and collapse a Popup control
+ SimpleScrollViewer to allow items to be scrolled and SimpleComboBoxItem to define the look of each item
+ The Popup shows a list of items in a StackPanel-->
+
+ <Style x:Key="SimpleComboBox" TargetType="{x:Type ComboBox}">
+ <Setter Property="SnapsToDevicePixels" Value="true"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ComboBox}">
+ <Grid>
+ <!-- The ToggleButton is databound to the ComboBox itself to toggle IsDropDownOpen -->
+ <ToggleButton Grid.Column="2" Template="{DynamicResource ComboBoxToggleButton}" x:Name="ToggleButton" Focusable="false" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press"/>
+ <ContentPresenter HorizontalAlignment="Left" Margin="3,3,23,3" x:Name="ContentSite" VerticalAlignment="Center" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" IsHitTestVisible="False"/>
+
+ <!-- The TextBox must be named PART_EditableTextBox or ComboBox will not recognize it -->
+ <TextBox Visibility="Hidden" Template="{DynamicResource ComboBoxTextBox}" HorizontalAlignment="Left" Margin="3,3,23,3" x:Name="PART_EditableTextBox" Style="{x:Null}" VerticalAlignment="Center" Focusable="True" Background="Transparent" IsReadOnly="{TemplateBinding IsReadOnly}"/>
+
+ <!-- The Popup shows the list of items in the ComboBox. IsOpen is databound to IsDropDownOpen which is toggled via the ComboBoxToggleButton -->
+ <Popup IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" x:Name="Popup" Focusable="False" AllowsTransparency="True" PopupAnimation="Slide">
+ <Grid MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}" x:Name="DropDown" SnapsToDevicePixels="True">
+ <Border x:Name="DropDownBorder" Background="{DynamicResource WindowBackgroundBrush}" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1"/>
+ <ScrollViewer Margin="4,6,4,6" Style="{DynamicResource SimpleScrollViewer}" SnapsToDevicePixels="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanContentScroll="True">
+
+ <!-- The StackPanel is used to display the children by setting IsItemsHost to be True -->
+ <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
+
+ </ScrollViewer>
+ </Grid>
+ </Popup>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <!-- This forces the DropDown to have a minimum size if it is empty -->
+ <Trigger Property="HasItems" Value="false">
+ <Setter Property="MinHeight" Value="95" TargetName="DropDownBorder"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ </Trigger>
+ <Trigger Property="IsGrouping" Value="true">
+ <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
+ </Trigger>
+ <Trigger Property="AllowsTransparency" SourceName="Popup" Value="true">
+ <Setter Property="CornerRadius" Value="4" TargetName="DropDownBorder"/>
+ <Setter Property="Margin" Value="0,2,0,0" TargetName="DropDownBorder"/>
+ </Trigger>
+ <Trigger Property="IsEditable" Value="true">
+ <Setter Property="IsTabStop" Value="false"/>
+ <Setter Property="Visibility" Value="Visible" TargetName="PART_EditableTextBox"/>
+ <Setter Property="Visibility" Value="Hidden" TargetName="ContentSite"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple ComboBoxItem - This is used for each item inside of the ComboBox. You can change the selected color of each item below-->
+ <Style x:Key="SimpleComboBoxItem" d:IsControlPart="True" TargetType="{x:Type ComboBoxItem}">
+ <Setter Property="SnapsToDevicePixels" Value="true"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ComboBoxItem}">
+ <Grid SnapsToDevicePixels="true">
+ <Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+
+ <!-- Change IsHighlighted SelectedBackgroundBrush to set the selection color for the items -->
+ <Trigger Property="IsHighlighted" Value="true">
+ <Setter Property="Background" Value="{DynamicResource SelectedBackgroundBrush}" TargetName="Border"/>
+ </Trigger>
+
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple SimpleProgressBar
+ The template uses two Border controls to show the Track and Progress
+ Limitations : It only supports a horizontal orientated ProgressBar -->
+ <Style x:Key="SimpleProgressBar" TargetType="{x:Type ProgressBar}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ProgressBar}">
+ <Grid>
+
+ <!-- This Border is the track. It must be named PART_Track -->
+ <Border x:Name="PART_Track" Background="{DynamicResource PressedBrush}" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1" CornerRadius="2"/>
+
+ <!-- This Border shows progress. It must be named PART_Indicator for the control to function -->
+ <Border HorizontalAlignment="Left" x:Name="PART_Indicator" Background="{DynamicResource MouseOverBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" BorderThickness="1" CornerRadius="2"/>
+
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple TextBox -->
+ <Style x:Key="SimpleTextBox" TargetType="{x:Type TextBox}">
+ <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
+ <Setter Property="AllowDrop" Value="true"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TextBox}">
+ <Grid>
+ <Border x:Name="Border" Background="{DynamicResource WindowBackgroundBrush}" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1" Padding="2" CornerRadius="2">
+
+ <!-- The implementation places the Content into the ScrollViewer. It must be named PART_ContentHost for the control to function -->
+ <ScrollViewer Margin="0" x:Name="PART_ContentHost" Style="{DynamicResource SimpleScrollViewer}" Background="{TemplateBinding Background}"/>
+
+ </Border>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple Label - This template is just a ContentPresenter that shows the content of the Label -->
+ <Style x:Key="SimpleLabel" TargetType="{x:Type Label}">
+ <Setter Property="HorizontalContentAlignment" Value="Left"/>
+ <Setter Property="VerticalContentAlignment" Value="Top"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Label}">
+ <Grid>
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsEnabled" Value="false"/>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple Menu - This template uses a StackPanel to layout MenuItems -->
+ <Style x:Key="SimpleMenu" TargetType="{x:Type Menu}">
+ <Setter Property="Background" Value="{DynamicResource LightBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
+ <Setter Property="SnapsToDevicePixels" Value="True"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Menu}">
+ <Grid>
+ <Border Margin="1" x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
+ <StackPanel Background="{TemplateBinding Background}" IsItemsHost="True" ClipToBounds="True" Orientation="Horizontal"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- This BitmapEffect is used by the Simple MenuItem -->
+ <DropShadowBitmapEffect x:Key="PopupDropShadow" ShadowDepth="1.5" Softness="0.15"/>
+
+ <!-- Simple MenuItem - The template uses triggers to provide four different arrangements of menu item which are set via the Role property -->
+ <Style x:Key="SimpleMenuItem" TargetType="{x:Type MenuItem}">
+ <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
+ <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
+ <Setter Property="Background" Value="Transparent"/>
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.MenuTextBrushKey}}"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type MenuItem}">
+ <Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
+ <Grid>
+
+ <!-- The Grid is used to hold together columns for an Icon, Content, Glyph checkmark and Arrow to show the next level
+ Size sharing is used in Grid so that the Icon, Content, Arrow for each MenuItem align together -->
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition MinWidth="17" Width="Auto" SharedSizeGroup="MenuItemIconColumnGroup"/>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIGTColumnGroup"/>
+ <ColumnDefinition Width="14"/>
+ </Grid.ColumnDefinitions>
+
+ <!-- ContentPresenter to show an Icon if needed -->
+ <ContentPresenter Margin="4,0,6,0" x:Name="Icon" VerticalAlignment="Center" ContentSource="Icon"/>
+
+ <!-- Glyph is a checkmark if needed for a checkable menu -->
+ <Grid Visibility="Hidden" Margin="4,0,6,0" x:Name="GlyphPanel" VerticalAlignment="Center">
+ <Path x:Name="GlyphPanelpath" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" Data="M0,2 L0,4.8 L2.5,7.4 L7.1,2.8 L7.1,0 L2.5,4.6 z" FlowDirection="LeftToRight"/>
+ </Grid>
+
+ <!-- Content for the menu text etc -->
+ <ContentPresenter Grid.Column="1" Margin="{TemplateBinding Padding}" x:Name="HeaderHost" RecognizesAccessKey="True" ContentSource="Header"/>
+
+ <!-- Arrow drawn path which points to the next level of the menu -->
+ <Grid Grid.Column="3" Margin="4,0,6,0" x:Name="ArrowPanel" VerticalAlignment="Center">
+ <Path x:Name="ArrowPanelPath" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" Data="M0,0 L0,8 L4,4 z"/>
+ </Grid>
+
+ <!-- The Popup is the body of the menu which expands down or across depending on the level of the item -->
+ <Popup IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" x:Name="SubMenuPopup" Focusable="false" AllowsTransparency="true" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" VerticalOffset="-3">
+ <Grid x:Name="SubMenu">
+ <Border x:Name="SubMenuBorder" Background="{DynamicResource WindowBackgroundBrush}" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1"/>
+
+ <!-- StackPanel holds children of the menu. This is set bu IsItemsHost=True -->
+ <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/>
+ </Grid>
+ </Popup>
+
+ </Grid>
+ </Border>
+
+ <!-- These triggers re-configure the four arrangements of MenuItem to show different levels of menu via Role -->
+ <ControlTemplate.Triggers>
+
+ <!-- Role = TopLevelHeader : this is the root menu item in a menu; the Popup expands down -->
+ <Trigger Property="Role" Value="TopLevelHeader">
+ <Setter Property="Margin" Value="0,1,0,1"/>
+ <Setter Property="Padding" Value="6,3,6,3"/>
+ <Setter Property="Grid.IsSharedSizeScope" Value="true"/>
+ <Setter Property="Placement" Value="Bottom" TargetName="SubMenuPopup"/>
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ArrowPanel"/>
+ </Trigger>
+
+ <!-- Role = TopLevelItem : this is a child menu item from the top level without any child items-->
+ <Trigger Property="Role" Value="TopLevelItem">
+ <Setter Property="Margin" Value="0,1,0,1"/>
+ <Setter Property="Padding" Value="6,3,6,3"/>
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ArrowPanel"/>
+ </Trigger>
+
+ <!-- Role = SubMenuHeader : this is a child menu item which does not have children -->
+ <Trigger Property="Role" Value="SubmenuHeader">
+ <Setter Property="DockPanel.Dock" Value="Top"/>
+ <Setter Property="Padding" Value="0,2,0,2"/>
+ <Setter Property="Grid.IsSharedSizeScope" Value="true"/>
+ </Trigger>
+
+ <!-- Role = SubMenuItem : this is a child menu item which has children-->
+ <Trigger Property="Role" Value="SubmenuItem">
+ <Setter Property="DockPanel.Dock" Value="Top"/>
+ <Setter Property="Padding" Value="0,2,0,2"/>
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ArrowPanel"/>
+ </Trigger>
+ <Trigger Property="IsSuspendingPopupAnimation" Value="true">
+ <Setter Property="PopupAnimation" Value="None" TargetName="SubMenuPopup"/>
+ </Trigger>
+
+ <!-- If no Icon is present the we collapse the Icon Content -->
+ <Trigger Property="Icon" Value="{x:Null}">
+ <Setter Property="Visibility" Value="Collapsed" TargetName="Icon"/>
+ </Trigger>
+
+ <!-- The GlyphPanel contains the CheckMark -->
+ <Trigger Property="IsChecked" Value="true">
+ <Setter Property="Visibility" Value="Visible" TargetName="GlyphPanel"/>
+ <Setter Property="Visibility" Value="Collapsed" TargetName="Icon"/>
+ </Trigger>
+
+ <Trigger Property="AllowsTransparency" SourceName="SubMenuPopup" Value="true">
+ <Setter Property="Margin" Value="0,0,3,3" TargetName="SubMenu"/>
+ <Setter Property="SnapsToDevicePixels" Value="true" TargetName="SubMenu"/>
+ <Setter Property="BitmapEffect" Value="{DynamicResource PopupDropShadow}" TargetName="SubMenuBorder"/>
+ </Trigger>
+
+ <!-- Using the system colors for the Menu Highlight and IsEnabled-->
+ <Trigger Property="IsHighlighted" Value="true">
+ <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" TargetName="Border"/>
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple Separator - This template is used for a Separator in a menu -->
+ <Style x:Key="SimpleSeparator" TargetType="{x:Type Separator}">
+ <Setter Property="Height" Value="1"/>
+ <Setter Property="Margin" Value="0,2,0,2"/>
+ <Setter Property="Focusable" Value="false"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Separator}">
+ <Border BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1"/>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Simple TabControl
+ This template uses Simple TabItem for each Tab. The TabItems are placed in the TabPanel
+ Limitations : The Simple TabControl only allow the Tabs to be shown at the top of the Tab control. You can re-position the TabPanel to change this-->
+
+ <Style x:Key="SimpleTabControl" TargetType="{x:Type TabControl}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TabControl}">
+ <Grid KeyboardNavigation.TabNavigation="Local">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*"/>
+ </Grid.RowDefinitions>
+
+ <!-- TabPanel is a layout container which allows the TabItems to wrap and re-order when selected
+ The implementation knows to use this control because it is marked IsItemsHost = True -->
+ <TabPanel Grid.Row="0" Margin="0,0,4,-1" x:Name="HeaderPanel" Background="Transparent" IsItemsHost="True" Panel.ZIndex="1" KeyboardNavigation.TabIndex="1"/>
+
+ <Border Grid.Row="1" x:Name="Border" Background="{DynamicResource WindowBackgroundBrush}" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1" CornerRadius="2" KeyboardNavigation.DirectionalNavigation="Contained" KeyboardNavigation.TabNavigation="Local" KeyboardNavigation.TabIndex="2">
+
+ <!-- The implementation switches the content. This control must be named PART_SelectedContentHost -->
+ <ContentPresenter Margin="4" x:Name="PART_SelectedContentHost" ContentSource="SelectedContent"/>
+
+ </Border>
+ </Grid>
+
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!--Simple TabItem -->
+ <Style x:Key="SimpleTabItem" d:IsControlPart="True" TargetType="{x:Type TabItem}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TabItem}">
+ <Grid>
+ <Border Margin="0,0,-4,0" x:Name="Border" Background="{DynamicResource LightBrush}" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1,1,1,1" CornerRadius="2,12,0,0">
+ <ContentPresenter HorizontalAlignment="Center" Margin="12,2,12,2" x:Name="ContentSite" VerticalAlignment="Center" RecognizesAccessKey="True" ContentSource="Header"/>
+ </Border>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+ <Setter Property="Panel.ZIndex" Value="100"/>
+ <Setter Property="Background" Value="{DynamicResource WindowBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderThickness" Value="1,1,1,0" TargetName="Border"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!--Simple Simple SliderThumb - The Thumb is the draggable part of a Slider-->
+ <Style x:Key="SimpleSliderThumb" d:IsControlPart="True" TargetType="{x:Type Thumb}">
+ <Setter Property="SnapsToDevicePixels" Value="true"/>
+ <Setter Property="Height" Value="14"/>
+ <Setter Property="Width" Value="14"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Thumb}">
+ <Grid>
+ <Ellipse x:Name="Ellipse" Fill="{DynamicResource NormalBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="True">
+ <Setter Property="Fill" Value="{DynamicResource MouseOverBrush}" TargetName="Ellipse"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Fill" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Ellipse"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!--Simple Simple Slider
+ Similiar to ScrollBar this template uses Track to layout the draggable Thumb which has an up and down repeat button
+ It uses Simple SliderThumb and SimpleScrollRepeatButtonStyle for the page up and down repeat buttons -->
+ <Style x:Key="SimpleSlider" TargetType="{x:Type Slider}">
+ <Setter Property="Background" Value="{DynamicResource LightBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Slider}">
+ <Grid x:Name="GridRoot">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+
+ <!-- TickBar shows the ticks for Slider -->
+ <TickBar Visibility="Collapsed" x:Name="TopTick" Height="4" SnapsToDevicePixels="True" Placement="Top" Fill="{DynamicResource GlyphBrush}"/>
+ <Border Grid.Row="1" Margin="0" x:Name="Border" Height="4" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"/>
+
+ <!-- The Track lays out the repeat buttons and thumb -->
+ <Track Grid.Row="1" x:Name="PART_Track">
+ <Track.Thumb>
+ <Thumb Style="{DynamicResource SimpleSliderThumb}"/>
+ </Track.Thumb>
+ <Track.IncreaseRepeatButton>
+ <RepeatButton Style="{DynamicResource SimpleScrollRepeatButtonStyle}" Command="Slider.IncreaseLarge"/>
+ </Track.IncreaseRepeatButton>
+ <Track.DecreaseRepeatButton>
+ <RepeatButton Style="{DynamicResource SimpleScrollRepeatButtonStyle}" Command="Slider.DecreaseLarge"/>
+ </Track.DecreaseRepeatButton>
+ </Track>
+
+ <TickBar Visibility="Collapsed" Grid.Row="2" x:Name="BottomTick" Height="4" SnapsToDevicePixels="True" Placement="Bottom" Fill="{TemplateBinding Foreground}"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="TickPlacement" Value="TopLeft">
+ <Setter Property="Visibility" Value="Visible" TargetName="TopTick"/>
+ </Trigger>
+ <Trigger Property="TickPlacement" Value="BottomRight">
+ <Setter Property="Visibility" Value="Visible" TargetName="BottomTick"/>
+ </Trigger>
+ <Trigger Property="TickPlacement" Value="Both">
+ <Setter Property="Visibility" Value="Visible" TargetName="TopTick"/>
+ <Setter Property="Visibility" Value="Visible" TargetName="BottomTick"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
+ </Trigger>
+
+ <!-- Use a rotation to create a Vertical Slider form the default Horizontal -->
+ <Trigger Property="Orientation" Value="Vertical">
+ <Setter Property="LayoutTransform" TargetName="GridRoot">
+ <Setter.Value>
+ <RotateTransform Angle="-90"/>
+ </Setter.Value>
+ </Setter>
+ <!-- Track rotates itself based on orientation so need to force it back -->
+ <Setter TargetName="PART_Track" Property="Orientation" Value="Horizontal"/>
+ </Trigger>
+
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!--Simple Tree View - This lays out TreeViewItems within a ScrollViewer -->
+ <Style x:Key="SimpleTreeView" TargetType="{x:Type TreeView}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TreeView}">
+ <Grid>
+ <Border x:Name="Border" Background="{DynamicResource WindowBackgroundBrush}" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1" CornerRadius="1">
+ <ScrollViewer Style="{DynamicResource SimpleScrollViewer}" Focusable="False" Background="{TemplateBinding Background}" Padding="4" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanContentScroll="False">
+ <ItemsPresenter/>
+ </ScrollViewer>
+ </Border>
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!--Simple TreeViewItem ToggleButton - + and - button to expand and collapse a TreeViewItem -->
+ <Style x:Key="SimpleTreeViewItemToggleButton" d:IsControlPart="True" TargetType="{x:Type ToggleButton}">
+ <Setter Property="Focusable" Value="False"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <Grid Width="15" Height="13" Background="Transparent">
+ <Path Visibility="Collapsed" HorizontalAlignment="Left" Margin="1,1,1,1" x:Name="IsExpandedPath" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 4 L 8 4 L 4 8 Z"/>
+ <Path HorizontalAlignment="Left" Margin="1,1,1,1" x:Name="ExpandPath" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 4 0 L 8 4 L 4 8 Z"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsChecked" Value="True">
+ <Setter Property="Visibility" Value="Visible" TargetName="IsExpandedPath"/>
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ExpandPath"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!--Simple TreeViewItem - The TreeViewItem template has a header which shows the Item and a Body which is an ItemsHost control which expands to show child items-->
+ <Style x:Key="SimpleTreeViewItem" d:IsControlPart="True" TargetType="{x:Type TreeViewItem}">
+ <Setter Property="Background" Value="Transparent"/>
+ <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
+ <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
+ <Setter Property="Padding" Value="1,0,0,0"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TreeViewItem}">
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition MinWidth="19" Width="Auto"/>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition/>
+ </Grid.RowDefinitions>
+ <ToggleButton x:Name="Expander" Style="{DynamicResource SimpleTreeViewItemToggleButton}" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press"/>
+ <Border Grid.Column="1" x:Name="Selection_Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" x:Name="PART_Header" ContentSource="Header"/>
+ </Border>
+ <ItemsPresenter Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1" x:Name="ItemsHost"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsExpanded" Value="false">
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ItemsHost"/>
+ </Trigger>
+ <Trigger Property="HasItems" Value="false">
+ <Setter Property="Visibility" Value="Hidden" TargetName="Expander"/>
+ </Trigger>
+ <Trigger Property="IsSelected" Value="true">
+ <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" TargetName="Selection_Border"/>
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsSelected" Value="true"/>
+ <Condition Property="IsSelectionActive" Value="false"/>
+ </MultiTrigger.Conditions>
+ <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" TargetName="Selection_Border"/>
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
+ </MultiTrigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <tb:TaskbarIcon x:Key="NotifyIcon" IconSource="ZeroTierIcon.ico" ToolTipText="ZeroTier One"/>
+</ResourceDictionary>
diff --git a/zto/windows/WinUI/Themes/Generic.xaml b/zto/windows/WinUI/Themes/Generic.xaml
new file mode 100644
index 0000000..2b47588
--- /dev/null
+++ b/zto/windows/WinUI/Themes/Generic.xaml
@@ -0,0 +1,7 @@
+<ResourceDictionary
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:local="clr-namespace:WinUI">
+
+
+</ResourceDictionary>
diff --git a/zto/windows/WinUI/ToolbarItem.xaml b/zto/windows/WinUI/ToolbarItem.xaml
new file mode 100644
index 0000000..3b064fc
--- /dev/null
+++ b/zto/windows/WinUI/ToolbarItem.xaml
@@ -0,0 +1,59 @@
+<Window x:Class="WinUI.ToolbarItem"
+ 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:WinUI"
+ xmlns:tb="http://www.hardcodet.net/taskbar"
+ xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
+ mc:Ignorable="d"
+ Height="300" Width="300" Visibility="Hidden" Name="Toolbar">
+
+ <Window.Resources>
+ <CollectionViewSource Source="{Binding ElementName=Toolbar, Path=NetworkCollection}" x:Key="KnownNetworks">
+ <CollectionViewSource.SortDescriptions>
+ <scm:SortDescription PropertyName="Header" Direction="Ascending"/>
+ </CollectionViewSource.SortDescriptions>
+ </CollectionViewSource>
+ </Window.Resources>
+
+ <Grid>
+ <tb:TaskbarIcon x:Name="MyNotifyIcon"
+ IconSource="ZeroTierIcon.ico"
+ ToolTipText="ZeroTier One"
+ MenuActivation="LeftOrRightClick">
+ <tb:TaskbarIcon.ContextMenu>
+ <ContextMenu>
+ <ContextMenu.ItemsSource>
+ <CompositeCollection>
+ <MenuItem Header="Node ID: unknown"
+ Click="ToolbarItem_NodeIDClicked"
+ x:Name="nodeIdMenuItem"/>
+ <Separator/>
+ <MenuItem Header="Join Network..."
+ Click="ToolbarItem_JoinNetworkClicked"/>
+ <MenuItem Header="Show Networks..."
+ Click="ToolbarItem_ShowNetworksClicked"/>
+ <Separator/>
+
+ <CollectionContainer Collection="{Binding Source={StaticResource KnownNetworks}}">
+
+ </CollectionContainer>
+
+ <Separator/>
+ <MenuItem Header="About..."
+ Click="ToolbarItem_AboutClicked"/>
+ <MenuItem Header="Preferences..."
+ Click="ToolbarItem_PreferencesClicked"/>
+ <Separator/>
+ <MenuItem Header="Quit"
+ Click="ToolbarItem_QuitClicked"/>
+
+ </CompositeCollection>
+ </ContextMenu.ItemsSource>
+ </ContextMenu>
+ </tb:TaskbarIcon.ContextMenu>
+
+ </tb:TaskbarIcon>
+ </Grid>
+</Window>
diff --git a/zto/windows/WinUI/ToolbarItem.xaml.cs b/zto/windows/WinUI/ToolbarItem.xaml.cs
new file mode 100644
index 0000000..29791a4
--- /dev/null
+++ b/zto/windows/WinUI/ToolbarItem.xaml.cs
@@ -0,0 +1,310 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+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 System.Text.RegularExpressions;
+using System.Timers;
+using System.Windows.Threading;
+using System.IO;
+using System.Diagnostics;
+using Microsoft.Win32;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for ToolbarItem.xaml
+ /// </summary>
+ public partial class ToolbarItem : Window, INotifyPropertyChanged
+ {
+ private APIHandler handler = APIHandler.Instance;
+
+ private Point netListLocation = new Point(0, 0);
+ private Point joinNetLocation = new Point(0, 0);
+ private Point aboutViewLocation = new Point(0, 0);
+ private Point prefsViewLocation = new Point(0, 0);
+
+ private NetworkListView netListView = new NetworkListView();
+ private JoinNetworkView joinNetView = null;
+ private AboutView aboutView = null;
+ private PreferencesView prefsView = null;
+
+ private NetworkMonitor mon = NetworkMonitor.Instance;
+
+ private ObservableCollection<MenuItem> _networkCollection = new ObservableCollection<MenuItem>();
+
+ public ObservableCollection<MenuItem> NetworkCollection
+ {
+ get { return _networkCollection; }
+ set { _networkCollection = value; }
+ }
+
+ private string nodeId;
+
+ public ToolbarItem()
+ {
+ InitializeComponent();
+
+ mon.SubscribeNetworkUpdates(updateNetworks);
+ mon.SubscribeStatusUpdates(updateStatus);
+
+ SystemEvents.DisplaySettingsChanged += new EventHandler(SystemEvents_DisplaySettingsChanged);
+ }
+
+ ~ToolbarItem()
+ {
+ mon.UnsubscribeNetworkUpdates(updateNetworks);
+ mon.UnsubscribeStatusUpdates(updateStatus);
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ private void updateNetworks(List<ZeroTierNetwork> networks)
+ {
+ if (networks != null)
+ {
+ this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ NetworkCollection.Clear();
+ foreach (ZeroTierNetwork n in networks)
+ {
+ MenuItem item = new MenuItem();
+ item.Header = n.Title;
+ item.DataContext = n;
+ item.IsChecked = n.IsConnected;
+ item.Click += ToolbarItem_NetworkClicked;
+
+ NetworkCollection.Add(item);
+ }
+ }));
+ }
+ }
+
+ private void updateStatus(ZeroTierStatus status)
+ {
+ if (status != null)
+ {
+ Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ nodeIdMenuItem.Header = "Node ID: " + status.Address;
+ nodeIdMenuItem.IsEnabled = true;
+ nodeId = status.Address;
+ }));
+ }
+ }
+
+ private void ToolbarItem_NodeIDClicked(object sender, System.Windows.RoutedEventArgs e)
+ {
+ Clipboard.SetText(nodeId);
+ }
+
+ private void ToolbarItem_ShowNetworksClicked(object sender, System.Windows.RoutedEventArgs e)
+ {
+ if (netListView == null)
+ {
+ netListView = new WinUI.NetworkListView();
+ netListView.Closed += ShowNetworksClosed;
+ }
+
+ bool netListNeedsMoving = true;
+ if (netListLocation.X > 0 && netListLocation.Y > 0)
+ {
+ netListView.Left = netListLocation.X;
+ netListView.Top = netListLocation.Y;
+ netListNeedsMoving = false;
+ }
+
+ netListView.Show();
+
+ if (netListNeedsMoving)
+ {
+ setWindowPosition(netListView);
+ netListLocation.X = netListView.Left;
+ netListLocation.Y = netListView.Top;
+ }
+
+ netListView.Activate();
+ }
+
+ private void ShowNetworksClosed(object sender, System.EventArgs e)
+ {
+ netListView = null;
+ }
+
+ private void ToolbarItem_JoinNetworkClicked(object sender, System.EventArgs e)
+ {
+ if (joinNetView == null)
+ {
+ joinNetView = new JoinNetworkView();
+ joinNetView.Closed += JoinNetworkClosed;
+
+ bool needsMove = true;
+ if (joinNetLocation.X > 0 && joinNetLocation.Y > 0)
+ {
+ joinNetView.Left = joinNetLocation.X;
+ joinNetView.Top = joinNetLocation.Y;
+ needsMove = false;
+ }
+
+ joinNetView.Show();
+
+ if (needsMove)
+ {
+ setWindowPosition(joinNetView);
+ joinNetLocation.X = joinNetView.Left;
+ joinNetLocation.Y = joinNetView.Top;
+ }
+ }
+ else
+ {
+ joinNetView.Activate();
+ }
+ }
+
+ private void JoinNetworkClosed(object sender, System.EventArgs e)
+ {
+ joinNetView = null;
+ }
+
+ private void ToolbarItem_AboutClicked(object sender, System.EventArgs e)
+ {
+ if (aboutView == null)
+ {
+ aboutView = new AboutView();
+ aboutView.Closed += AboutClosed;
+
+ bool needsMove = true;
+ if (aboutViewLocation.X > 0 && aboutViewLocation.Y > 0)
+ {
+ aboutView.Left = aboutViewLocation.X;
+ aboutView.Top = aboutViewLocation.Y;
+ needsMove = false;
+ }
+
+ aboutView.Show();
+
+ if (needsMove)
+ {
+ setWindowPosition(aboutView);
+ aboutViewLocation.X = aboutView.Left;
+ aboutViewLocation.Y = aboutView.Top;
+ }
+ }
+ else
+ {
+ aboutView.Activate();
+ }
+ }
+
+ private void AboutClosed(object sender, System.EventArgs e)
+ {
+ aboutView = null;
+ }
+
+ private void ToolbarItem_PreferencesClicked(object sender, System.EventArgs e)
+ {
+ if (prefsView == null)
+ {
+ prefsView = new PreferencesView();
+ prefsView.Closed += PreferencesClosed;
+
+ bool needsMove = true;
+ if (prefsViewLocation.X > 0 && prefsViewLocation.Y > 0)
+ {
+ prefsView.Left = prefsViewLocation.X;
+ prefsView.Top = prefsViewLocation.Y;
+ needsMove = false;
+ }
+
+ prefsView.Show();
+
+ if (needsMove)
+ {
+ setWindowPosition(prefsView);
+ prefsViewLocation.X = prefsView.Left;
+ prefsViewLocation.Y = prefsView.Top;
+ }
+ }
+ else
+ {
+ prefsView.Activate();
+ }
+ }
+
+ private void PreferencesClosed(object sender, System.EventArgs e)
+ {
+ prefsView = null;
+ }
+
+ private void ToolbarItem_QuitClicked(object sender, System.EventArgs e)
+ {
+ NetworkMonitor.Instance.StopMonitor();
+ this.Close();
+ Application.Current.Shutdown();
+ }
+
+ private void ToolbarItem_NetworkClicked(object sender, System.Windows.RoutedEventArgs e)
+ {
+ if(sender.GetType() == typeof(MenuItem))
+ {
+ MenuItem item = e.Source as MenuItem;
+ if (item.DataContext != null)
+ {
+ ZeroTierNetwork network = item.DataContext as ZeroTierNetwork;
+ if (item.IsChecked)
+ {
+ APIHandler.Instance.LeaveNetwork(network.NetworkId);
+ }
+ else
+ {
+ APIHandler.Instance.JoinNetwork(network.NetworkId, network.AllowManaged, network.AllowGlobal, network.AllowDefault);
+ }
+ }
+ }
+ }
+
+ private void setWindowPosition(Window w)
+ {
+ double width = w.ActualWidth;
+ double height = w.ActualHeight;
+
+ double screenHeight = SystemParameters.PrimaryScreenHeight;
+ double screenWidth = SystemParameters.PrimaryScreenWidth;
+
+ double top = screenHeight - height - 40;
+ double left = screenWidth - width - 20;
+
+ w.Top = top;
+ w.Left = left;
+ }
+
+ private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
+ {
+ // reset cached locations to (0, 0) when display size changes
+ netListLocation.X = 0;
+ netListLocation.Y = 0;
+ joinNetLocation.X = 0;
+ joinNetLocation.Y = 0;
+ aboutViewLocation.X = 0;
+ aboutViewLocation.Y = 0;
+ prefsViewLocation.X = 0;
+ prefsViewLocation.Y = 0;
+ }
+ }
+}
diff --git a/zto/windows/WinUI/WinUI.csproj b/zto/windows/WinUI/WinUI.csproj
new file mode 100644
index 0000000..6b8013e
--- /dev/null
+++ b/zto/windows/WinUI/WinUI.csproj
@@ -0,0 +1,265 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}</ProjectGuid>
+ <OutputType>WinExe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>WinUI</RootNamespace>
+ <AssemblyName>ZeroTier One</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <WarningLevel>4</WarningLevel>
+ <ExpressionBlendVersion>5.0.40218.0</ExpressionBlendVersion>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <AutorunEnabled>true</AutorunEnabled>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.0</ApplicationVersion>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>true</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup>
+ <StartupObject>WinUI.App</StartupObject>
+ </PropertyGroup>
+ <PropertyGroup>
+ <ApplicationIcon>ZeroTierIcon.ico</ApplicationIcon>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SignAssembly>false</SignAssembly>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SignManifests>false</SignManifests>
+ </PropertyGroup>
+ <PropertyGroup />
+ <ItemGroup>
+ <Reference Include="Accessibility" />
+ <Reference Include="Hardcodet.Wpf.TaskbarNotification, Version=1.0.5.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\Hardcodet.NotifyIcon.Wpf.1.0.8\lib\net45\Hardcodet.Wpf.TaskbarNotification.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+ <HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="PresentationUI, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
+ <Reference Include="ReachFramework" />
+ <Reference Include="System" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Deployment" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Printing" />
+ <Reference Include="System.Windows.Forms" />
+ <Reference Include="System.Xml" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="System.Xaml">
+ <RequiredTargetFramework>4.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="UIAutomationProvider" />
+ <Reference Include="UIAutomationTypes" />
+ <Reference Include="WindowsBase" />
+ <Reference Include="PresentationCore" />
+ <Reference Include="PresentationFramework" />
+ </ItemGroup>
+ <ItemGroup>
+ <ApplicationDefinition Include="App.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </ApplicationDefinition>
+ <Compile Include="AboutView.xaml.cs">
+ <DependentUpon>AboutView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="JoinNetworkView.xaml.cs">
+ <DependentUpon>JoinNetworkView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="NetworkMonitor.cs" />
+ <Compile Include="NetworkRoute.cs" />
+ <Compile Include="NetworksPage.xaml.cs">
+ <DependentUpon>NetworksPage.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="PeersPage.xaml.cs">
+ <DependentUpon>PeersPage.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="PreferencesView.xaml.cs">
+ <DependentUpon>PreferencesView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="ToolbarItem.xaml.cs">
+ <DependentUpon>ToolbarItem.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="ZeroTierPeerPhysicalPath.cs" />
+ <Compile Include="ZeroTierPeer.cs" />
+ <Compile Include="ZeroTierNetwork.cs" />
+ <Compile Include="ZeroTierStatus.cs" />
+ <Page Include="AboutView.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="JoinNetworkView.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="NetworkListView.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </Page>
+ <Compile Include="APIHandler.cs" />
+ <Compile Include="App.xaml.cs">
+ <DependentUpon>App.xaml</DependentUpon>
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="NetworkListView.xaml.cs">
+ <DependentUpon>NetworkListView.xaml</DependentUpon>
+ <SubType>Code</SubType>
+ </Compile>
+ <Page Include="NetworkInfoView.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="NetworksPage.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="PeersPage.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="PreferencesView.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="Simple Styles.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </Page>
+ <Page Include="Themes\Generic.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </Page>
+ <Page Include="ToolbarItem.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="NetworkInfoView.xaml.cs">
+ <DependentUpon>NetworkInfoView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="Properties\AssemblyInfo.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="Properties\Resources.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Resources.resx</DependentUpon>
+ </Compile>
+ <Compile Include="Properties\Settings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Settings.settings</DependentUpon>
+ <DesignTimeSharedInput>True</DesignTimeSharedInput>
+ </Compile>
+ <EmbeddedResource Include="Properties\Resources.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ <None Include="app.manifest" />
+ <None Include="packages.config" />
+ <None Include="Properties\Settings.settings">
+ <Generator>SettingsSingleFileGenerator</Generator>
+ <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+ </None>
+ <AppDesigner Include="Properties\" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include=".NETFramework,Version=v4.5">
+ <Visible>False</Visible>
+ <ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <ItemGroup>
+ <BlendEmbeddedFont Include="Fonts\segoeui.ttf">
+ <IsSystemFont>True</IsSystemFont>
+ <All>True</All>
+ <AutoFill>True</AutoFill>
+ </BlendEmbeddedFont>
+ <BlendEmbeddedFont Include="Fonts\segoeuib.ttf">
+ <IsSystemFont>True</IsSystemFont>
+ <All>True</All>
+ <AutoFill>True</AutoFill>
+ </BlendEmbeddedFont>
+ <BlendEmbeddedFont Include="Fonts\segoeuii.ttf">
+ <IsSystemFont>True</IsSystemFont>
+ <All>True</All>
+ <AutoFill>True</AutoFill>
+ </BlendEmbeddedFont>
+ <BlendEmbeddedFont Include="Fonts\segoeuiz.ttf">
+ <IsSystemFont>True</IsSystemFont>
+ <All>True</All>
+ <AutoFill>True</AutoFill>
+ </BlendEmbeddedFont>
+ <Resource Include="ZeroTierIcon.ico" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Resources\ZeroTierIcon.ico" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(MSBuildExtensionsPath)\Microsoft\Expression\Blend\.NETFramework\v4.5\Microsoft.Expression.Blend.WPF.targets" />
+ <PropertyGroup>
+ <PostBuildEvent>copy "$(SolutionDir)\copyutil\bin\$(ConfigurationName)\copyutil.exe" "$(ProjectDir)\$(OutDir)\copyutil.exe"</PostBuildEvent>
+ </PropertyGroup>
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/zto/windows/WinUI/ZeroTierIcon.ico b/zto/windows/WinUI/ZeroTierIcon.ico
new file mode 100644
index 0000000..2d190c4
--- /dev/null
+++ b/zto/windows/WinUI/ZeroTierIcon.ico
Binary files differ
diff --git a/zto/windows/WinUI/ZeroTierNetwork.cs b/zto/windows/WinUI/ZeroTierNetwork.cs
new file mode 100644
index 0000000..d680238
--- /dev/null
+++ b/zto/windows/WinUI/ZeroTierNetwork.cs
@@ -0,0 +1,494 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ [Serializable]
+ public class ZeroTierNetwork : ISerializable, IEquatable<ZeroTierNetwork>, IComparable<ZeroTierNetwork>, INotifyPropertyChanged
+ {
+ private string networkId;
+ private string macAddress;
+ private string networkName;
+ private string networkStatus;
+ private string networkType;
+ private Int32 mtu;
+ private bool dhcp;
+ private bool bridge;
+ private bool broadcastEnabled;
+ private Int32 portError;
+ private Int32 netconfRevision;
+ private string[] assignedAddresses;
+ private NetworkRoute[] routes;
+ private string deviceName;
+ private bool allowManaged;
+ private bool allowGlobal;
+ private bool allowDefault;
+ private bool isConnected;
+
+ protected ZeroTierNetwork(SerializationInfo info, StreamingContext ctx)
+ {
+ try
+ {
+ NetworkId = info.GetString("nwid");
+ MacAddress = info.GetString("mac");
+ NetworkName = info.GetString("name");
+ NetworkStatus = info.GetString("status");
+ NetworkType = info.GetString("type");
+ MTU = info.GetInt32("mtu");
+ DHCP = info.GetBoolean("dhcp");
+ Bridge = info.GetBoolean("bridge");
+ BroadcastEnabled = info.GetBoolean("broadcastEnabled");
+ PortError = info.GetInt32("portError");
+ NetconfRevision = info.GetInt32("netconfRevision");
+ AssignedAddresses = (string[])info.GetValue("assignedAddresses", typeof(string[]));
+ Routes = (NetworkRoute[])info.GetValue("routes", typeof(NetworkRoute[]));
+ DeviceName = info.GetString("portDeviceName");
+ AllowManaged = info.GetBoolean("allowManaged");
+ AllowGlobal = info.GetBoolean("allowGlobal");
+ AllowDefault = info.GetBoolean("allowDefault");
+ }
+ catch { }
+ IsConnected = false;
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext ctx)
+ {
+ info.AddValue("nwid", NetworkId);
+ info.AddValue("mac", MacAddress);
+ info.AddValue("name", NetworkName);
+ info.AddValue("status", NetworkStatus);
+ info.AddValue("type", NetworkType);
+ info.AddValue("mtu", MTU);
+ info.AddValue("dhcp", DHCP);
+ info.AddValue("bridge", Bridge);
+ info.AddValue("broadcastEnabled", BroadcastEnabled);
+ info.AddValue("portError", PortError);
+ info.AddValue("netconfRevision", NetconfRevision);
+ info.AddValue("assignedAddresses", AssignedAddresses);
+ info.AddValue("routes", Routes);
+ info.AddValue("portDeviceName", DeviceName);
+ info.AddValue("allowManaged", AllowManaged);
+ info.AddValue("allowGlobal", AllowGlobal);
+ info.AddValue("allowDefault", AllowDefault);
+ }
+
+ public void UpdateNetwork(ZeroTierNetwork network)
+ {
+ if (network == null)
+ return;
+
+ if (!NetworkId.Equals(network.NetworkId))
+ {
+ NetworkId = network.NetworkId;
+ }
+
+ if (!MacAddress.Equals(network.MacAddress))
+ {
+ MacAddress = network.MacAddress;
+ }
+
+ if (!NetworkName.Equals(network.NetworkName))
+ {
+ NetworkName = network.NetworkName;
+ }
+
+ if (!NetworkStatus.Equals(network.NetworkStatus))
+ {
+ NetworkStatus = network.NetworkStatus;
+ }
+
+ if (!NetworkType.Equals(network.NetworkType))
+ {
+ NetworkType = network.NetworkType;
+ }
+
+ if (MTU != network.MTU)
+ {
+ MTU = network.MTU;
+ }
+
+ if (DHCP != network.DHCP)
+ {
+ DHCP = network.DHCP;
+ }
+
+ if (Bridge != network.Bridge)
+ {
+ Bridge = network.Bridge;
+ }
+
+ if (BroadcastEnabled != network.BroadcastEnabled)
+ {
+ BroadcastEnabled = network.BroadcastEnabled;
+ }
+
+ if (PortError != network.PortError)
+ {
+ PortError = network.PortError;
+ }
+
+ if (NetconfRevision != network.NetconfRevision)
+ {
+ NetconfRevision = network.NetconfRevision;
+ }
+
+ AssignedAddresses = network.AssignedAddresses;
+
+ Routes = network.Routes;
+
+ if (!DeviceName.Equals(network.DeviceName))
+ {
+ DeviceName = network.DeviceName;
+ }
+
+ if (AllowManaged != network.AllowManaged)
+ {
+ AllowManaged = network.AllowManaged;
+ }
+
+ if (AllowGlobal != network.AllowGlobal)
+ {
+ AllowGlobal = network.AllowGlobal;
+ }
+
+ if (AllowDefault != network.AllowDefault)
+ {
+ AllowDefault = network.AllowDefault;
+ }
+
+ if (IsConnected != network.IsConnected)
+ {
+ IsConnected = network.IsConnected;
+ }
+ }
+
+ protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ [JsonProperty("nwid")]
+ public string NetworkId {
+ get
+ {
+ return networkId;
+ }
+ set
+ {
+ networkId = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("mac")]
+ public string MacAddress
+ {
+ get
+ {
+ return macAddress;
+ }
+ set
+ {
+ macAddress = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("name")]
+ public string NetworkName
+ {
+ get
+ {
+ return networkName;
+ }
+ set
+ {
+ networkName = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("status")]
+ public string NetworkStatus
+ {
+ get
+ {
+ return networkStatus;
+ }
+ set
+ {
+ networkStatus = value;
+ NotifyPropertyChanged();
+ }
+
+ }
+
+ [JsonProperty("type")]
+ public string NetworkType
+ {
+ get
+ {
+ return networkType;
+ }
+ set
+ {
+ networkType = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("mtu")]
+ public int MTU
+ {
+ get
+ {
+ return mtu;
+ }
+ set
+ {
+ mtu = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("dhcp")]
+ public bool DHCP
+ {
+ get
+ {
+ return dhcp;
+ }
+ set
+ {
+ dhcp = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("bridge")]
+ public bool Bridge
+ {
+ get
+ {
+ return bridge;
+ }
+ set
+ {
+ bridge = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("broadcastEnabled")]
+ public bool BroadcastEnabled
+ {
+ get
+ {
+ return broadcastEnabled;
+ }
+ set
+ {
+ broadcastEnabled = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("portError")]
+ public int PortError
+ {
+ get
+ {
+ return portError;
+ }
+ set
+ {
+ portError = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("netconfRevision")]
+ public int NetconfRevision
+ {
+ get
+ {
+ return netconfRevision;
+ }
+ set
+ {
+ netconfRevision = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("assignedAddresses")]
+ public string[] AssignedAddresses
+ {
+ get
+ {
+ return assignedAddresses;
+ }
+ set
+ {
+ assignedAddresses = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("routes")]
+ public NetworkRoute[] Routes
+ {
+ get
+ {
+ return routes;
+ }
+ set
+ {
+ routes = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("portDeviceName")]
+ public string DeviceName
+ {
+ get
+ {
+ return deviceName;
+ }
+ set
+ {
+ deviceName = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("allowManaged")]
+ public bool AllowManaged
+ {
+ get
+ {
+ return allowManaged;
+ }
+ set
+ {
+ allowManaged = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("allowGlobal")]
+ public bool AllowGlobal
+ {
+ get
+ {
+ return allowGlobal;
+ }
+ set
+ {
+ allowGlobal = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("allowDefault")]
+ public bool AllowDefault
+ {
+ get
+ {
+ return allowDefault;
+ }
+ set
+ {
+ allowDefault = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public bool IsConnected
+ {
+ get
+ {
+ return isConnected;
+ }
+ set
+ {
+ isConnected = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public String Title
+ {
+ get
+ {
+
+ if (NetworkName != null && NetworkName.Length > 0)
+ {
+ return NetworkId + " (" + NetworkName + ")";
+ }
+ else
+ {
+ return NetworkId;
+ }
+ }
+ }
+
+ public bool Equals(ZeroTierNetwork network)
+ {
+ if (NetworkId == null || network == null)
+ return false;
+
+ return NetworkId.Equals(network.NetworkId);
+ }
+
+ public int CompareTo(ZeroTierNetwork network)
+ {
+ if (NetworkId == null || network == null)
+ return -1;
+
+ UInt64 thisNwid = UInt64.Parse(NetworkId, System.Globalization.NumberStyles.HexNumber);
+ UInt64 otherNwid = UInt64.Parse(network.NetworkId, System.Globalization.NumberStyles.HexNumber);
+
+ if (thisNwid > otherNwid)
+ {
+ return 1;
+ }
+ else if (thisNwid < otherNwid)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+ public class NetworkEqualityComparer : IEqualityComparer<ZeroTierNetwork>
+ {
+ public bool Equals(ZeroTierNetwork lhs, ZeroTierNetwork rhs)
+ {
+ if (lhs.NetworkId.Equals(rhs.NetworkId))
+ {
+ lhs.UpdateNetwork(rhs);
+ return true;
+ }
+ return false;
+ }
+
+ public int GetHashCode(ZeroTierNetwork obj)
+ {
+ return obj.NetworkId.GetHashCode();
+ }
+ }
+}
diff --git a/zto/windows/WinUI/ZeroTierPeer.cs b/zto/windows/WinUI/ZeroTierPeer.cs
new file mode 100644
index 0000000..1597cf2
--- /dev/null
+++ b/zto/windows/WinUI/ZeroTierPeer.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ public class ZeroTierPeer : IEquatable<ZeroTierPeer>
+ {
+ [JsonProperty("address")]
+ public string Address { get; set; }
+
+ private Int64 _lastUnicast;
+ [JsonProperty("lastUnicastFrame")]
+ public Int64 LastUnicastFrame
+ {
+ get
+ {
+ if (_lastUnicast == 0)
+ return 0;
+
+ TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
+ Int64 millisecondsSinceEpoch = (Int64)t.TotalMilliseconds;
+ return (millisecondsSinceEpoch - _lastUnicast) / 1000;
+ }
+ set
+ {
+ _lastUnicast = value;
+ }
+ }
+
+ private Int64 _lastMulticast;
+ [JsonProperty("lastMulticastFrame")]
+ public Int64 LastMulticastFrame
+ {
+ get
+ {
+ if (_lastMulticast == 0)
+ return 0;
+
+ TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
+ Int64 millisecondsSinceEpoch = (Int64)t.TotalMilliseconds;
+ return (millisecondsSinceEpoch - _lastMulticast) / 1000;
+ }
+ set
+ {
+ _lastMulticast = value;
+ }
+ }
+
+ [JsonProperty("versionMajor")]
+ public int VersionMajor { get; set; }
+
+ [JsonProperty("versionMinor")]
+ public int VersionMinor { get; set; }
+
+ [JsonProperty("versionRev")]
+ public int VersionRev { get; set; }
+
+ [JsonProperty("version")]
+ public string Version { get; set; }
+
+ public string VersionString
+ {
+ get
+ {
+ if (Version == "-1.-1.-1")
+ return "-";
+ else
+ return Version;
+ }
+ }
+
+ [JsonProperty("latency")]
+ public string Latency { get; set; }
+
+ [JsonProperty("role")]
+ public string Role { get; set; }
+
+ [JsonProperty("paths")]
+ public List<ZeroTierPeerPhysicalPath> Paths { get; set; }
+
+ public string DataPaths
+ {
+ get
+ {
+ string pathStr = "";
+ foreach(ZeroTierPeerPhysicalPath path in Paths)
+ {
+ pathStr += path.Address + "\n";
+ }
+ return pathStr;
+ }
+ }
+
+ public bool Equals(ZeroTierPeer other)
+ {
+ return this.Address.Equals(other.Address, StringComparison.InvariantCultureIgnoreCase);
+ }
+
+ public void Update(ZeroTierPeer other)
+ {
+ _lastUnicast = other._lastUnicast;
+ _lastMulticast = other._lastMulticast;
+ VersionMajor = other.VersionMajor;
+ VersionMinor = other.VersionMinor;
+ VersionRev = other.VersionRev;
+ Version = other.Version;
+ Latency = other.Latency;
+ Role = other.Role;
+ Paths = other.Paths;
+ }
+ }
+}
diff --git a/zto/windows/WinUI/ZeroTierPeerPhysicalPath.cs b/zto/windows/WinUI/ZeroTierPeerPhysicalPath.cs
new file mode 100644
index 0000000..3eaa802
--- /dev/null
+++ b/zto/windows/WinUI/ZeroTierPeerPhysicalPath.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ public class ZeroTierPeerPhysicalPath
+ {
+ [JsonProperty("address")]
+ public string Address { get; set; }
+
+ [JsonProperty("lastSend")]
+ public UInt64 LastSend { get; set; }
+
+ [JsonProperty("lastReceive")]
+ public UInt64 LastReceive { get; set; }
+
+ [JsonProperty("fixed")]
+ public bool Fixed { get; set; }
+
+ [JsonProperty("preferred")]
+ public bool Preferred { get; set; }
+ }
+}
diff --git a/zto/windows/WinUI/ZeroTierStatus.cs b/zto/windows/WinUI/ZeroTierStatus.cs
new file mode 100644
index 0000000..2851d01
--- /dev/null
+++ b/zto/windows/WinUI/ZeroTierStatus.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ public class ZeroTierStatus
+ {
+ [JsonProperty("address")]
+ public string Address { get; set; }
+
+ [JsonProperty("publicIdentity")]
+ public string PublicIdentity { get; set; }
+
+ [JsonProperty("online")]
+ public bool Online { get; set; }
+
+ [JsonProperty("tcpFallbackActive")]
+ public bool TcpFallbackActive { get; set; }
+
+ [JsonProperty("versionMajor")]
+ public int VersionMajor { get; set; }
+
+ [JsonProperty("versionMinor")]
+ public int VersionMinor { get; set; }
+
+ [JsonProperty("versionRev")]
+ public int VersionRev { get; set; }
+
+ [JsonProperty("version")]
+ public string Version { get; set; }
+
+ [JsonProperty("clock")]
+ public UInt64 Clock { get; set; }
+ }
+}
diff --git a/zto/windows/WinUI/app.manifest b/zto/windows/WinUI/app.manifest
new file mode 100644
index 0000000..b537bf4
--- /dev/null
+++ b/zto/windows/WinUI/app.manifest
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <!-- UAC Manifest Options
+ If you want to change the Windows User Account Control level replace the
+ requestedExecutionLevel node with one of the following.
+
+ <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+ <requestedExecutionLevel level="highestAvailable" uiAccess="false" />
+
+ Specifying requestedExecutionLevel node will disable file and registry virtualization.
+ If you want to utilize File and Registry Virtualization for backward
+ compatibility then delete the requestedExecutionLevel node.
+ -->
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- A list of all Windows versions that this application is designed to work with.
+ Windows will automatically select the most compatible environment.-->
+
+ <!-- If your application is designed to work with Windows Vista, uncomment the following supportedOS node-->
+ <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"></supportedOS>-->
+
+ <!-- If your application is designed to work with Windows 7, uncomment the following supportedOS node-->
+ <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>-->
+
+ <!-- If your application is designed to work with Windows 8, uncomment the following supportedOS node-->
+ <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS>-->
+
+ </application>
+ </compatibility>
+
+ <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
+ <!-- <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+ </dependency>-->
+
+</asmv1:assembly>
diff --git a/zto/windows/WinUI/packages.config b/zto/windows/WinUI/packages.config
new file mode 100644
index 0000000..e6803f8
--- /dev/null
+++ b/zto/windows/WinUI/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Hardcodet.NotifyIcon.Wpf" version="1.0.8" targetFramework="net45" />
+ <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
+</packages> \ No newline at end of file