Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4918 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
33 changed files with 4857 additions and 166 deletions
@ -0,0 +1,29 @@ |
|||||||
|
<UserControl x:Class="ICSharpCode.StartPage.RecentProjectsControl" |
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||||
|
xmlns:core="http://icsharpcode.net/sharpdevelop/core"> |
||||||
|
<StackPanel Orientation="Vertical"> |
||||||
|
<ListView Name="lastProjectsListView" SelectionMode="Single" MouseDoubleClick="lastProjectsDoubleClick" KeyDown="lastProjectsKeyDown"> |
||||||
|
<ListView.Resources> |
||||||
|
<DataTemplate x:Key="nameCellTemplate"> |
||||||
|
<TextBlock> |
||||||
|
<Hyperlink Tag="{Binding}" Click="listViewHyperlinkClick"> |
||||||
|
<TextBlock Text="{Binding Path=Name}" /> |
||||||
|
</Hyperlink> |
||||||
|
</TextBlock> |
||||||
|
</DataTemplate> |
||||||
|
</ListView.Resources> |
||||||
|
<ListView.View> |
||||||
|
<GridView> |
||||||
|
<GridViewColumn CellTemplate="{StaticResource nameCellTemplate}" Header="{core:Localize Global.Name}" /> |
||||||
|
<GridViewColumn DisplayMemberBinding="{Binding Path=LastModification}" Header="{core:Localize StartPage.StartMenu.ModifiedTable}" /> |
||||||
|
<GridViewColumn DisplayMemberBinding="{Binding Path=Path}" Header="{core:Localize StartPage.StartMenu.LocationTable}" /> |
||||||
|
</GridView> |
||||||
|
</ListView.View> |
||||||
|
</ListView> |
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,20,0,0"> |
||||||
|
<Button Content="{core:Localize StartPage.StartMenu.OpenCombineButton}" Click="openSolutionClick" /> |
||||||
|
<Button Content="{core:Localize StartPage.StartMenu.NewCombineButton}" Click="newSolutionClick" Margin="8,0,0,0" /> |
||||||
|
</StackPanel> |
||||||
|
</StackPanel> |
||||||
|
</UserControl> |
||||||
@ -0,0 +1,97 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Documents; |
||||||
|
using System.Windows.Input; |
||||||
|
|
||||||
|
using ICSharpCode.Core.Presentation; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Project; |
||||||
|
|
||||||
|
namespace ICSharpCode.StartPage |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for RecentProjectsControl.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class RecentProjectsControl : UserControl |
||||||
|
{ |
||||||
|
public RecentProjectsControl() |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
|
||||||
|
SetBinding(HeaderProperty, new LocalizeExtension("StartPage.StartMenu.BarNameName").CreateBinding()); |
||||||
|
BuildRecentProjectList(); |
||||||
|
} |
||||||
|
|
||||||
|
public static readonly DependencyProperty HeaderProperty = HeaderedContentControl.HeaderProperty.AddOwner(typeof(RecentProjectsControl)); |
||||||
|
|
||||||
|
public object Header { |
||||||
|
get { return GetValue(HeaderProperty); } |
||||||
|
set { SetValue(HeaderProperty, value); } |
||||||
|
} |
||||||
|
|
||||||
|
void BuildRecentProjectList() |
||||||
|
{ |
||||||
|
List<RecentOpenItem> items = new List<RecentOpenItem>(); |
||||||
|
foreach (string path in FileService.RecentOpen.RecentProject) { |
||||||
|
FileInfo file = new FileInfo(path); |
||||||
|
if (file.Exists) { |
||||||
|
items.Add( |
||||||
|
new RecentOpenItem { |
||||||
|
Name = System.IO.Path.GetFileNameWithoutExtension(path), |
||||||
|
LastModification = file.LastWriteTime.ToShortDateString(), |
||||||
|
Path = path |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
lastProjectsListView.ItemsSource = items; |
||||||
|
} |
||||||
|
|
||||||
|
class RecentOpenItem |
||||||
|
{ |
||||||
|
public string Name { get; set; } |
||||||
|
public string LastModification { get; set; } |
||||||
|
public string Path { get; set; } |
||||||
|
} |
||||||
|
|
||||||
|
void lastProjectsDoubleClick(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
RecentOpenItem item = (RecentOpenItem)lastProjectsListView.SelectedItem; |
||||||
|
if (item != null) { |
||||||
|
ProjectService.LoadSolutionOrProject(item.Path); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void lastProjectsKeyDown(object sender, KeyEventArgs e) |
||||||
|
{ |
||||||
|
if (e.Key == Key.Return) { |
||||||
|
lastProjectsDoubleClick(null, null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void listViewHyperlinkClick(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
RecentOpenItem item = (RecentOpenItem)((Hyperlink)sender).Tag; |
||||||
|
ProjectService.LoadSolutionOrProject(item.Path); |
||||||
|
} |
||||||
|
|
||||||
|
void openSolutionClick(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
new ICSharpCode.SharpDevelop.Project.Commands.LoadSolution().Run(); |
||||||
|
} |
||||||
|
|
||||||
|
void newSolutionClick(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
new ICSharpCode.SharpDevelop.Project.Commands.CreateNewSolution().Run(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,144 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.Core; |
||||||
|
using ICSharpCode.Core.Services; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using System.IO; |
||||||
|
|
||||||
|
namespace ICSharpCode.UsageDataCollector |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Main singleton class of the analytics. This class is thread-safe.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AnalyticsMonitor : IAnalyticsMonitor |
||||||
|
{ |
||||||
|
public static readonly AnalyticsMonitor Instance = new AnalyticsMonitor(); |
||||||
|
|
||||||
|
public static bool EnabledIsUndecided { |
||||||
|
get { |
||||||
|
return string.IsNullOrEmpty(PropertyService.Get("ICSharpCode.UsageDataCollector.Enabled")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static bool Enabled { |
||||||
|
get { |
||||||
|
return string.Equals(PropertyService.Get("ICSharpCode.UsageDataCollector.Enabled"), bool.TrueString, StringComparison.OrdinalIgnoreCase); |
||||||
|
} |
||||||
|
set { |
||||||
|
PropertyService.Set("ICSharpCode.UsageDataCollector.Enabled", value.ToString()); |
||||||
|
if (value) { |
||||||
|
Instance.OpenSession(); |
||||||
|
} else { |
||||||
|
Instance.CloseSession(); |
||||||
|
Instance.TryDeleteDatabase(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
readonly object lockObj = new object(); |
||||||
|
string dbFileName; |
||||||
|
AnalyticsSessionWriter session; |
||||||
|
|
||||||
|
private AnalyticsMonitor() |
||||||
|
{ |
||||||
|
var container = ServiceManager.Instance.GetRequiredService<ThreadSafeServiceContainer>(); |
||||||
|
container.TryAddService(typeof(IAnalyticsMonitor), this); |
||||||
|
dbFileName = Path.Combine(PropertyService.ConfigDirectory, "usageData.dat"); |
||||||
|
|
||||||
|
SharpDevelop.Gui.WorkbenchSingleton.WorkbenchUnloaded += delegate { CloseSession(); }; |
||||||
|
} |
||||||
|
|
||||||
|
public void OpenSession() |
||||||
|
{ |
||||||
|
lock (lockObj) { |
||||||
|
if (session == null) { |
||||||
|
try { |
||||||
|
session = new AnalyticsSessionWriter(dbFileName); |
||||||
|
} catch (DatabaseTooNewException) { |
||||||
|
LoggingService.Warn("Could not use AnalyticsMonitor: too new version of database"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TryDeleteDatabase() |
||||||
|
{ |
||||||
|
lock (lockObj) { |
||||||
|
CloseSession(); |
||||||
|
try { |
||||||
|
File.Delete(dbFileName); |
||||||
|
} catch (IOException) { |
||||||
|
} catch (AccessViolationException) { |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void CloseSession() |
||||||
|
{ |
||||||
|
lock (lockObj) { |
||||||
|
if (session != null) { |
||||||
|
session.Dispose(); |
||||||
|
session = null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void TrackException(Exception exception) |
||||||
|
{ |
||||||
|
lock (lockObj) { |
||||||
|
if (session != null) { |
||||||
|
// TODO: recognize inner exceptions
|
||||||
|
session.AddException(exception.GetType().FullName, exception.StackTrace); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IAnalyticsMonitorTrackedFeature TrackFeature(string featureName, string activationMethod) |
||||||
|
{ |
||||||
|
TrackedFeature feature = new TrackedFeature(); |
||||||
|
lock (lockObj) { |
||||||
|
if (session != null) { |
||||||
|
feature.IsTracked = true; |
||||||
|
feature.FeatureID = session.AddFeatureUse(featureName, activationMethod); |
||||||
|
} |
||||||
|
} |
||||||
|
return feature; |
||||||
|
} |
||||||
|
|
||||||
|
void EndTracking(TrackedFeature feature) |
||||||
|
{ |
||||||
|
lock (lockObj) { |
||||||
|
if (session != null && feature.IsTracked) { |
||||||
|
feature.IsTracked = false; |
||||||
|
session.WriteEndTimeForFeature(feature.FeatureID); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sealed class TrackedFeature : IAnalyticsMonitorTrackedFeature |
||||||
|
{ |
||||||
|
internal bool IsTracked; |
||||||
|
internal long FeatureID; |
||||||
|
|
||||||
|
public void EndTracking() |
||||||
|
{ |
||||||
|
Instance.EndTracking(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class AutoStartCommand : AbstractCommand |
||||||
|
{ |
||||||
|
public override void Run() |
||||||
|
{ |
||||||
|
if (AnalyticsMonitor.Enabled) |
||||||
|
AnalyticsMonitor.Instance.OpenSession(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,189 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using ICSharpCode.Core; |
||||||
|
using System; |
||||||
|
using System.Data.SQLite; |
||||||
|
using System.Runtime.Serialization; |
||||||
|
|
||||||
|
namespace ICSharpCode.UsageDataCollector |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Creates an analytics session.
|
||||||
|
/// </summary>
|
||||||
|
public class AnalyticsSessionWriter : IDisposable |
||||||
|
{ |
||||||
|
SQLiteConnection connection; |
||||||
|
long sessionID; |
||||||
|
|
||||||
|
public AnalyticsSessionWriter(string databaseFileName) |
||||||
|
{ |
||||||
|
SQLiteConnectionStringBuilder conn = new SQLiteConnectionStringBuilder(); |
||||||
|
conn.Add("Data Source", databaseFileName); |
||||||
|
|
||||||
|
connection = new SQLiteConnection(conn.ConnectionString); |
||||||
|
connection.Open(); |
||||||
|
InitializeTables(); |
||||||
|
|
||||||
|
StartSession(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates or upgrades the database
|
||||||
|
/// </summary>
|
||||||
|
void InitializeTables() |
||||||
|
{ |
||||||
|
using (SQLiteTransaction transaction = this.connection.BeginTransaction()) { |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = @"
|
||||||
|
CREATE TABLE IF NOT EXISTS Properties ( |
||||||
|
name TEXT NOT NULL PRIMARY KEY, |
||||||
|
value TEXT NOT NULL |
||||||
|
); |
||||||
|
INSERT OR IGNORE INTO Properties (name, value) VALUES ('dbVersion', '1.0'); |
||||||
|
INSERT OR IGNORE INTO Properties (name, value) VALUES ('userID', '" + Guid.NewGuid().ToString() + @"'); |
||||||
|
";
|
||||||
|
cmd.ExecuteNonQuery(); |
||||||
|
} |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = "SELECT value FROM Properties WHERE name = 'dbVersion';"; |
||||||
|
string version = (string)cmd.ExecuteScalar(); |
||||||
|
if (version == null) |
||||||
|
throw new InvalidOperationException("Error retrieving database version"); |
||||||
|
if (version != "1.0") { |
||||||
|
throw new DatabaseTooNewException(); |
||||||
|
} |
||||||
|
} |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = @"
|
||||||
|
CREATE TABLE IF NOT EXISTS Sessions ( |
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |
||||||
|
startTime TEXT NOT NULL, |
||||||
|
endTime TEXT, |
||||||
|
appVersion TEXT, |
||||||
|
platform TEXT, |
||||||
|
osVersion TEXT, |
||||||
|
processorCount INTEGER, |
||||||
|
dotnetRuntime TEXT, |
||||||
|
language TEXT |
||||||
|
); |
||||||
|
CREATE TABLE IF NOT EXISTS FeatureUses ( |
||||||
|
session INTEGER NOT NULL, |
||||||
|
time TEXT NOT NULL, |
||||||
|
endTime TEXT, |
||||||
|
feature TEXT NOT NULL, |
||||||
|
activationMethod TEXT |
||||||
|
); |
||||||
|
CREATE TABLE IF NOT EXISTS Exceptions ( |
||||||
|
session INTEGER NOT NULL, |
||||||
|
time TEXT NOT NULL, |
||||||
|
type TEXT NOT NULL, |
||||||
|
stackTrace TEXT |
||||||
|
); |
||||||
|
";
|
||||||
|
cmd.ExecuteNonQuery(); |
||||||
|
} |
||||||
|
transaction.Commit(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void StartSession() |
||||||
|
{ |
||||||
|
using (SQLiteTransaction transaction = this.connection.BeginTransaction()) { |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = "INSERT INTO Sessions (startTime, appVersion, platform, osVersion, processorCount, dotnetRuntime, language)" + |
||||||
|
" VALUES (datetime(), ?, ?, ?, ?, ?, ?);"; |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = RevisionClass.FullVersion }); |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = Environment.OSVersion.Platform.ToString() }); |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = Environment.OSVersion.Version.ToString() }); |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = Environment.ProcessorCount }); |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = Environment.Version.ToString() }); |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = ICSharpCode.Core.ResourceService.Language }); |
||||||
|
cmd.ExecuteNonQuery(); |
||||||
|
} |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = "SELECT last_insert_rowid();"; |
||||||
|
sessionID = (long)cmd.ExecuteScalar(); |
||||||
|
} |
||||||
|
transaction.Commit(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void EndSession() |
||||||
|
{ |
||||||
|
using (SQLiteTransaction transaction = this.connection.BeginTransaction()) { |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = "UPDATE Sessions SET endTime = datetime() WHERE id = ?;"; |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = sessionID }); |
||||||
|
cmd.ExecuteNonQuery(); |
||||||
|
} |
||||||
|
transaction.Commit(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public long AddFeatureUse(string featureName, string activationMethod) |
||||||
|
{ |
||||||
|
long featureRowId; |
||||||
|
using (SQLiteTransaction transaction = this.connection.BeginTransaction()) { |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = "INSERT INTO FeatureUses (session, time, feature, activationMethod)" + |
||||||
|
" VALUES (?, datetime(), ?, ?);"; |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = sessionID }); |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = featureName }); |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = activationMethod }); |
||||||
|
cmd.ExecuteNonQuery(); |
||||||
|
} |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = "SELECT last_insert_rowid();"; |
||||||
|
featureRowId = (long)cmd.ExecuteScalar(); |
||||||
|
} |
||||||
|
transaction.Commit(); |
||||||
|
} |
||||||
|
return featureRowId; |
||||||
|
} |
||||||
|
|
||||||
|
public void WriteEndTimeForFeature(long featureID) |
||||||
|
{ |
||||||
|
using (SQLiteTransaction transaction = this.connection.BeginTransaction()) { |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = "UPDATE FeatureUses SET endTime = datetime() WHERE ROWID = ?;"; |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = featureID }); |
||||||
|
cmd.ExecuteNonQuery(); |
||||||
|
} |
||||||
|
transaction.Commit(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void AddException(string exceptionType, string stacktrace) |
||||||
|
{ |
||||||
|
using (SQLiteTransaction transaction = this.connection.BeginTransaction()) { |
||||||
|
using (SQLiteCommand cmd = this.connection.CreateCommand()) { |
||||||
|
cmd.CommandText = "INSERT INTO Exceptions (session, time, type, stackTrace)" + |
||||||
|
" VALUES (?, datetime(), ?, ?);"; |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = sessionID }); |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = exceptionType }); |
||||||
|
cmd.Parameters.Add(new SQLiteParameter { Value = stacktrace }); |
||||||
|
cmd.ExecuteNonQuery(); |
||||||
|
} |
||||||
|
transaction.Commit(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void Dispose() |
||||||
|
{ |
||||||
|
EndSession(); |
||||||
|
connection.Close(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[Serializable] |
||||||
|
public class DatabaseTooNewException : Exception |
||||||
|
{ |
||||||
|
public DatabaseTooNewException() {} |
||||||
|
protected DatabaseTooNewException(SerializationInfo info, StreamingContext context) : base(info, context) {} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
#region Using directives
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// 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("UsageDataCollector")] |
||||||
|
[assembly: AssemblyDescription("Collects usage data and sends it to the SharpDevelop team")] |
||||||
|
[assembly: AssemblyConfiguration("")] |
||||||
|
[assembly: AssemblyTrademark("")] |
||||||
|
[assembly: AssemblyCulture("")] |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<gui:OptionPanel x:Class="ICSharpCode.UsageDataCollector.OptionPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:gui="clr-namespace:ICSharpCode.SharpDevelop.Gui;assembly=ICSharpCode.SharpDevelop" xmlns:core="http://icsharpcode.net/sharpdevelop/core" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||||
|
<StackPanel> |
||||||
|
<TextBlock TextWrapping="Wrap"> |
||||||
|
We need your help! To improve SharpDevelop in the future, we would like to know which features are used most.<LineBreak /> |
||||||
|
SharpDevelop can collect this information and upload it automatically. |
||||||
|
</TextBlock> |
||||||
|
<RadioButton Name="acceptRadio">I would like to participate - collect and upload usage data</RadioButton> |
||||||
|
<RadioButton Name="declineRadio">I do not want to participate</RadioButton> |
||||||
|
<TextBlock HorizontalAlignment="Left"> |
||||||
|
<Hyperlink NavigateUri="http://www.example.com/UsageData">Privacy Statement</Hyperlink> |
||||||
|
</TextBlock> |
||||||
|
</StackPanel> |
||||||
|
</gui:OptionPanel> |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.SharpDevelop.Gui; |
||||||
|
|
||||||
|
namespace ICSharpCode.UsageDataCollector |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for OptionPage.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class OptionPage : OptionPanel |
||||||
|
{ |
||||||
|
public OptionPage() |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
} |
||||||
|
|
||||||
|
public override void LoadOptions() |
||||||
|
{ |
||||||
|
base.LoadOptions(); |
||||||
|
if (!AnalyticsMonitor.EnabledIsUndecided) { |
||||||
|
if (AnalyticsMonitor.Enabled) |
||||||
|
acceptRadio.IsChecked = true; |
||||||
|
else |
||||||
|
declineRadio.IsChecked = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override bool SaveOptions() |
||||||
|
{ |
||||||
|
if (acceptRadio.IsChecked ?? false) |
||||||
|
AnalyticsMonitor.Enabled = true; |
||||||
|
else if (declineRadio.IsChecked ?? false) |
||||||
|
AnalyticsMonitor.Enabled = false; |
||||||
|
return base.SaveOptions(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,49 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<UserControl |
||||||
|
x:Class="ICSharpCode.UsageDataCollector.StartPageMessage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:core="http://icsharpcode.net/sharpdevelop/core"> |
||||||
|
<StackPanel> |
||||||
|
<core:CollapsiblePanel |
||||||
|
Name="mainPanel"> |
||||||
|
<StackPanel> |
||||||
|
<TextBlock |
||||||
|
TextWrapping="Wrap"> |
||||||
|
We need your help! To improve SharpDevelop in the future, we would like to know which features are used most.<LineBreak /> |
||||||
|
SharpDevelop can collect this information and upload it automatically. |
||||||
|
</TextBlock> |
||||||
|
<RadioButton |
||||||
|
Name="acceptRadio" |
||||||
|
Checked="Radio_Checked">I would like to participate - collect and upload usage data</RadioButton> |
||||||
|
<RadioButton |
||||||
|
Name="declineRadio" |
||||||
|
Checked="Radio_Checked">I do not want to participate</RadioButton> |
||||||
|
<Button |
||||||
|
Name="saveButton" |
||||||
|
IsEnabled="False" |
||||||
|
HorizontalAlignment="Left" |
||||||
|
Margin="16 4" |
||||||
|
Width="75" |
||||||
|
Height="23" |
||||||
|
Click="SaveButton_Click">Save</Button> |
||||||
|
</StackPanel> |
||||||
|
</core:CollapsiblePanel> |
||||||
|
<core:CollapsiblePanel |
||||||
|
Name="acceptedMessage" |
||||||
|
IsCollapsed="True"> |
||||||
|
<TextBlock> |
||||||
|
Thank you for participating! You can still opt-out later using the SharpDevelop options. |
||||||
|
</TextBlock> |
||||||
|
</core:CollapsiblePanel> |
||||||
|
<core:CollapsiblePanel |
||||||
|
Name="declinedMessage" |
||||||
|
IsCollapsed="True"> |
||||||
|
<TextBlock> |
||||||
|
You can still opt-in later using the SharpDevelop options. |
||||||
|
</TextBlock> |
||||||
|
</core:CollapsiblePanel> |
||||||
|
<TextBlock |
||||||
|
HorizontalAlignment="Left"> |
||||||
|
<Hyperlink |
||||||
|
NavigateUri="http://www.example.com/UsageData">Privacy Statement</Hyperlink> |
||||||
|
</TextBlock> |
||||||
|
</StackPanel> |
||||||
|
</UserControl> |
||||||
@ -0,0 +1,53 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Text; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Data; |
||||||
|
using System.Windows.Documents; |
||||||
|
using System.Windows.Input; |
||||||
|
using System.Windows.Media; |
||||||
|
|
||||||
|
namespace ICSharpCode.UsageDataCollector |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for StartPageMessage.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class StartPageMessage : UserControl |
||||||
|
{ |
||||||
|
public StartPageMessage() |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
|
||||||
|
this.Header = "Usage Data Collection"; |
||||||
|
} |
||||||
|
|
||||||
|
void Radio_Checked(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
saveButton.IsEnabled = true; |
||||||
|
} |
||||||
|
|
||||||
|
void SaveButton_Click(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
bool accepted = acceptRadio.IsChecked ?? false; |
||||||
|
AnalyticsMonitor.Enabled = accepted; |
||||||
|
mainPanel.IsCollapsed = true; |
||||||
|
acceptedMessage.IsCollapsed = !accepted; |
||||||
|
declinedMessage.IsCollapsed = accepted; |
||||||
|
} |
||||||
|
|
||||||
|
public static readonly DependencyProperty HeaderProperty = HeaderedContentControl.HeaderProperty.AddOwner(typeof(StartPageMessage)); |
||||||
|
|
||||||
|
public object Header { |
||||||
|
get { return GetValue(HeaderProperty); } |
||||||
|
set { SetValue(HeaderProperty, value); } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,35 @@ |
|||||||
|
<AddIn name = "Usage Data Collector" |
||||||
|
author = "Daniel Grunwald" |
||||||
|
url = "" |
||||||
|
description = "Collects usage data and sends it to the SharpDevelop team" |
||||||
|
addInManagerHidden = "preinstalled"> |
||||||
|
|
||||||
|
<Manifest> |
||||||
|
<Identity name = "ICSharpCode.UsageDataCollector"/> |
||||||
|
</Manifest> |
||||||
|
|
||||||
|
<Runtime> |
||||||
|
<Import assembly = "UsageDataCollector.dll"/> |
||||||
|
</Runtime> |
||||||
|
|
||||||
|
<Path name = "/SharpDevelop/ViewContent/StartPage/Items"> |
||||||
|
<Condition name = "Compare" string = "${property:ICSharpCode.UsageDataCollector.Enabled}" equals = ""> |
||||||
|
<Class id = "UsageDataCollector" |
||||||
|
insertbefore = "RecentProjects" |
||||||
|
class = "ICSharpCode.UsageDataCollector.StartPageMessage"/> |
||||||
|
</Condition> |
||||||
|
</Path> |
||||||
|
|
||||||
|
<Path name = "/Workspace/Autostart"> |
||||||
|
<Condition name = "Compare" string = "${property:ICSharpCode.UsageDataCollector.Enabled}" equals = "True"> |
||||||
|
<Class id = "UsageDataCollector" |
||||||
|
class = "ICSharpCode.UsageDataCollector.AutoStartCommand"/> |
||||||
|
</Condition> |
||||||
|
</Path> |
||||||
|
|
||||||
|
<Path name="/SharpDevelop/Dialogs/OptionsDialog/UIOptions"> |
||||||
|
<OptionPanel id = "UsageDataCollector" |
||||||
|
label = "Usage Data Collector" |
||||||
|
class = "ICSharpCode.UsageDataCollector.OptionPage" /> |
||||||
|
</Path> |
||||||
|
</AddIn> |
||||||
@ -0,0 +1,101 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> |
||||||
|
<PropertyGroup> |
||||||
|
<ProjectGuid>{6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0}</ProjectGuid> |
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||||
|
<OutputType>Library</OutputType> |
||||||
|
<RootNamespace>ICSharpCode.UsageDataCollector</RootNamespace> |
||||||
|
<AssemblyName>UsageDataCollector</AssemblyName> |
||||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> |
||||||
|
<OutputPath>..\..\..\..\AddIns\AddIns\Misc\UsageDataCollector\</OutputPath> |
||||||
|
<AllowUnsafeBlocks>False</AllowUnsafeBlocks> |
||||||
|
<NoStdLib>False</NoStdLib> |
||||||
|
<WarningLevel>4</WarningLevel> |
||||||
|
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' "> |
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget> |
||||||
|
<RegisterForComInterop>False</RegisterForComInterop> |
||||||
|
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies> |
||||||
|
<BaseAddress>4194304</BaseAddress> |
||||||
|
<FileAlignment>4096</FileAlignment> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> |
||||||
|
<DebugSymbols>true</DebugSymbols> |
||||||
|
<DebugType>Full</DebugType> |
||||||
|
<Optimize>False</Optimize> |
||||||
|
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow> |
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||||
|
</PropertyGroup> |
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> |
||||||
|
<DebugSymbols>False</DebugSymbols> |
||||||
|
<DebugType>None</DebugType> |
||||||
|
<Optimize>True</Optimize> |
||||||
|
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> |
||||||
|
<DefineConstants>TRACE</DefineConstants> |
||||||
|
</PropertyGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Reference Include="PresentationCore"> |
||||||
|
<RequiredTargetFramework>3.0</RequiredTargetFramework> |
||||||
|
</Reference> |
||||||
|
<Reference Include="PresentationFramework"> |
||||||
|
<RequiredTargetFramework>3.0</RequiredTargetFramework> |
||||||
|
</Reference> |
||||||
|
<Reference Include="System" /> |
||||||
|
<Reference Include="System.Data" /> |
||||||
|
<Reference Include="System.Data.SQLite"> |
||||||
|
<HintPath>..\..\..\Libraries\SQLite\System.Data.SQLite.dll</HintPath> |
||||||
|
<Private>True</Private> |
||||||
|
</Reference> |
||||||
|
<Reference Include="System.Drawing" /> |
||||||
|
<Reference Include="System.Windows.Forms" /> |
||||||
|
<Reference Include="System.Xml" /> |
||||||
|
<Reference Include="WindowsBase"> |
||||||
|
<RequiredTargetFramework>3.0</RequiredTargetFramework> |
||||||
|
</Reference> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<None Include="UsageDataCollector.addin"> |
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory> |
||||||
|
</None> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Compile Include="..\..\..\Main\GlobalAssemblyInfo.cs"> |
||||||
|
<Link>Configuration\GlobalAssemblyInfo.cs</Link> |
||||||
|
</Compile> |
||||||
|
<Compile Include="AnalyticsMonitor.cs" /> |
||||||
|
<Compile Include="AnalyticsSessionWriter.cs" /> |
||||||
|
<Compile Include="Configuration\AssemblyInfo.cs" /> |
||||||
|
<Compile Include="OptionPage.xaml.cs"> |
||||||
|
<DependentUpon>OptionPage.xaml</DependentUpon> |
||||||
|
<SubType>Code</SubType> |
||||||
|
</Compile> |
||||||
|
<Compile Include="StartPageMessage.xaml.cs"> |
||||||
|
<DependentUpon>StartPageMessage.xaml</DependentUpon> |
||||||
|
<SubType>Code</SubType> |
||||||
|
</Compile> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<Page Include="OptionPage.xaml" /> |
||||||
|
<Page Include="StartPageMessage.xaml" /> |
||||||
|
</ItemGroup> |
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj"> |
||||||
|
<Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project> |
||||||
|
<Name>ICSharpCode.SharpDevelop</Name> |
||||||
|
<Private>False</Private> |
||||||
|
</ProjectReference> |
||||||
|
<ProjectReference Include="..\..\..\Main\Core\Project\ICSharpCode.Core.csproj"> |
||||||
|
<Project>{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}</Project> |
||||||
|
<Name>ICSharpCode.Core</Name> |
||||||
|
<Private>False</Private> |
||||||
|
</ProjectReference> |
||||||
|
<ProjectReference Include="..\..\..\Main\ICSharpCode.Core.Presentation\ICSharpCode.Core.Presentation.csproj"> |
||||||
|
<Project>{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}</Project> |
||||||
|
<Name>ICSharpCode.Core.Presentation</Name> |
||||||
|
<Private>False</Private> |
||||||
|
</ProjectReference> |
||||||
|
</ItemGroup> |
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> |
||||||
|
</Project> |
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,84 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using ICSharpCode.Core.Services; |
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.Core |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Allows marking the end-time of feature uses.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Implementations of this interface must be thread-safe.</remarks>
|
||||||
|
public interface IAnalyticsMonitorTrackedFeature |
||||||
|
{ |
||||||
|
void EndTracking(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows tracking feature use.
|
||||||
|
/// All methods on this class are thread-safe.
|
||||||
|
/// </summary>
|
||||||
|
public static class AnalyticsMonitorService |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Tracks an exception that has occurred.
|
||||||
|
/// </summary>
|
||||||
|
public static void TrackException(Exception exception) |
||||||
|
{ |
||||||
|
if (exception == null) |
||||||
|
throw new ArgumentNullException("exception"); |
||||||
|
IAnalyticsMonitor monitor = ServiceManager.Instance.GetService<IAnalyticsMonitor>(); |
||||||
|
if (monitor != null) { |
||||||
|
monitor.TrackException(exception); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tracks a feature use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="featureName">Name of the feature</param>
|
||||||
|
/// <returns>Object that can be used to 'end' the feature use, if measuring time spans is desired.</returns>
|
||||||
|
public static IAnalyticsMonitorTrackedFeature TrackFeature(string featureName) |
||||||
|
{ |
||||||
|
return TrackFeature(featureName, null); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tracks a feature use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="featureName">Name of the feature</param>
|
||||||
|
/// <param name="activationMethod">Method used to 'activate' the feature (e.g. Menu, Toolbar, Shortcut, etc.)</param>
|
||||||
|
/// <returns>Object that can be used to 'end' the feature use, if measuring time spans is desired.</returns>
|
||||||
|
public static IAnalyticsMonitorTrackedFeature TrackFeature(string featureName, string activationMethod) |
||||||
|
{ |
||||||
|
if (featureName == null) |
||||||
|
throw new ArgumentNullException("featureName"); |
||||||
|
|
||||||
|
if (activationMethod != null) |
||||||
|
LoggingService.Debug("Activated feature '" + featureName + "', activation=" + activationMethod); |
||||||
|
else |
||||||
|
LoggingService.Debug("Activated feature '" + featureName + "'"); |
||||||
|
|
||||||
|
IAnalyticsMonitor monitor = ServiceManager.Instance.GetService<IAnalyticsMonitor>(); |
||||||
|
if (monitor != null) { |
||||||
|
return monitor.TrackFeature(featureName, activationMethod) ?? DummyFeature.Instance; |
||||||
|
} else { |
||||||
|
return DummyFeature.Instance; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sealed class DummyFeature : IAnalyticsMonitorTrackedFeature |
||||||
|
{ |
||||||
|
public static readonly DummyFeature Instance = new DummyFeature(); |
||||||
|
|
||||||
|
public void EndTracking() |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
|
||||||
|
namespace ICSharpCode.Core.Services |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Interface for AnalyticsMonitorService.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Implementations of this interface must be thread-safe.</remarks>
|
||||||
|
public interface IAnalyticsMonitor |
||||||
|
{ |
||||||
|
void TrackException(Exception exception); |
||||||
|
IAnalyticsMonitorTrackedFeature TrackFeature(string featureName, string activationMethod); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,101 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Data; |
||||||
|
using System.Windows.Media.Animation; |
||||||
|
|
||||||
|
namespace ICSharpCode.Core.Presentation |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Allows animated collapsing of the content of this panel.
|
||||||
|
/// </summary>
|
||||||
|
public class CollapsiblePanel : ContentControl |
||||||
|
{ |
||||||
|
static CollapsiblePanel() |
||||||
|
{ |
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(CollapsiblePanel), |
||||||
|
new FrameworkPropertyMetadata(typeof(CollapsiblePanel))); |
||||||
|
} |
||||||
|
|
||||||
|
public static readonly DependencyProperty IsCollapsedProperty = DependencyProperty.Register( |
||||||
|
"IsCollapsed", typeof(bool), typeof(CollapsiblePanel), |
||||||
|
new UIPropertyMetadata(false, new PropertyChangedCallback(OnIsCollapsedChanged))); |
||||||
|
|
||||||
|
public bool IsCollapsed { |
||||||
|
get { return (bool)GetValue(IsCollapsedProperty); } |
||||||
|
set { SetValue(IsCollapsedProperty, value); } |
||||||
|
} |
||||||
|
|
||||||
|
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register( |
||||||
|
"Duration", typeof(TimeSpan), typeof(CollapsiblePanel), |
||||||
|
new UIPropertyMetadata(TimeSpan.FromMilliseconds(500))); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The duration in milliseconds of the animation.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan Duration { |
||||||
|
get { return (TimeSpan)GetValue(DurationProperty); } |
||||||
|
set { SetValue(DurationProperty, value); } |
||||||
|
} |
||||||
|
|
||||||
|
protected internal static readonly DependencyProperty AnimationProgressProperty = DependencyProperty.Register( |
||||||
|
"AnimationProgress", typeof(double), typeof(CollapsiblePanel), |
||||||
|
new FrameworkPropertyMetadata(1.0)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value between 0 and 1 specifying how far the animation currently is.
|
||||||
|
/// </summary>
|
||||||
|
protected internal double AnimationProgress { |
||||||
|
get { return (double)GetValue(AnimationProgressProperty); } |
||||||
|
set { SetValue(AnimationProgressProperty, value); } |
||||||
|
} |
||||||
|
|
||||||
|
static void OnIsCollapsedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
||||||
|
{ |
||||||
|
((CollapsiblePanel)d).SetupAnimation((bool)e.NewValue); |
||||||
|
} |
||||||
|
|
||||||
|
void SetupAnimation(bool isCollapsed) |
||||||
|
{ |
||||||
|
if (this.IsLoaded) { |
||||||
|
// If the animation is already running, calculate remaining portion of the time
|
||||||
|
double currentProgress = AnimationProgress; |
||||||
|
if (!isCollapsed) { |
||||||
|
currentProgress = 1.0 - currentProgress; |
||||||
|
} |
||||||
|
|
||||||
|
DoubleAnimation animation = new DoubleAnimation(); |
||||||
|
animation.To = isCollapsed ? 0.0 : 1.0; |
||||||
|
animation.Duration = TimeSpan.FromSeconds(Duration.TotalSeconds * currentProgress); |
||||||
|
animation.FillBehavior = FillBehavior.HoldEnd; |
||||||
|
|
||||||
|
this.BeginAnimation(AnimationProgressProperty, animation); |
||||||
|
} else { |
||||||
|
this.AnimationProgress = isCollapsed ? 0.0 : 1.0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sealed class CollapsiblePanelProgressToVisibilityConverter : IValueConverter |
||||||
|
{ |
||||||
|
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) |
||||||
|
{ |
||||||
|
if (value is double) |
||||||
|
return (double)value > 0 ? Visibility.Visible : Visibility.Collapsed; |
||||||
|
else |
||||||
|
return Visibility.Visible; |
||||||
|
} |
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue