项目结构调整

This commit is contained in:
艾竹
2023-04-16 20:11:40 +08:00
parent cbfbf96033
commit 81f91f3f35
2124 changed files with 218 additions and 5516 deletions

View File

@@ -0,0 +1,109 @@
#nullable enable
// ReSharper disable once CheckNamespace
namespace Fluent
{
using System;
using System.Windows;
using System.Windows.Data;
/// <summary>
/// Container class for KeyTip informations
/// </summary>
public class KeyTipInformation
{
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="keys">The keys to be used for <see cref="KeyTip"/>.</param>
/// <param name="associatedElement">The element to which this instance belongs to.</param>
/// <param name="hide">Defines if the created <see cref="KeyTip"/> should be hidden or not.</param>
public KeyTipInformation(string keys, FrameworkElement associatedElement, bool hide)
{
if (string.IsNullOrEmpty(keys))
{
throw new ArgumentNullException(nameof(keys));
}
if (associatedElement is null)
{
throw new ArgumentNullException(nameof(associatedElement));
}
this.Keys = keys;
this.AssociatedElement = associatedElement;
this.VisualTarget = this.AssociatedElement;
this.DefaultVisibility = hide
? Visibility.Collapsed
: Visibility.Visible;
this.KeyTip = new KeyTip
{
Content = keys,
Visibility = this.DefaultVisibility
};
// Bind IsEnabled property
var binding = new Binding(nameof(this.AssociatedElement.IsEnabled))
{
Source = this.AssociatedElement,
Mode = BindingMode.OneWay
};
this.KeyTip.SetBinding(UIElement.IsEnabledProperty, binding);
}
/// <summary>
/// Gets <see cref="Fluent.KeyTip.KeysProperty"/>
/// </summary>
public string Keys { get; }
/// <summary>
/// Gets the element this instance belongs to.
/// </summary>
public FrameworkElement AssociatedElement { get; }
/// <summary>
/// Gets or sets the element which acts as the visual target.
/// </summary>
public FrameworkElement VisualTarget { get; set; }
/// <summary>
/// Gets the initial visibility.
/// </summary>
public Visibility DefaultVisibility { get; }
/// <summary>
/// Gets the <see cref="Fluent.KeyTip"/> for <see cref="AssociatedElement"/>.
/// </summary>
public KeyTip KeyTip { get; }
/// <summary>
/// Gets or sets the position of <see cref="KeyTip"/>.
/// </summary>
public Point Position { get; set; }
/// <summary>
/// Gets or sets the backed up value of <see cref="System.Windows.Visibility"/> of <see cref="KeyTip"/>
/// </summary>
public Visibility BackupVisibility { get; set; }
/// <summary>
/// Gets <see cref="UIElement.IsVisible" /> from <see cref="KeyTip"/>.
/// </summary>
public bool IsVisible => this.KeyTip.IsVisible;
/// <summary>
/// Gets or sets <see cref="UIElement.Visibility" /> from <see cref="KeyTip"/>.
/// </summary>
public Visibility Visibility
{
get { return this.KeyTip.Visibility; }
set { this.KeyTip.Visibility = value; }
}
/// <summary>
/// Gets <see cref="UIElement.IsEnabled" /> from <see cref="KeyTip"/>
/// </summary>
public bool IsEnabled => this.KeyTip.IsEnabled;
}
}

View File

@@ -0,0 +1,39 @@
// ReSharper disable once CheckNamespace
namespace Fluent
{
/// <summary>
/// Represents the result of <see cref="IKeyTipedControl.OnKeyTipPressed"/>.
/// </summary>
public class KeyTipPressedResult
{
/// <summary>
/// An empty default instance.
/// </summary>
public static readonly KeyTipPressedResult Empty = new KeyTipPressedResult();
private KeyTipPressedResult()
{
}
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="pressedElementAquiredFocus">Defines if the pressed element aquired focus or not.</param>
/// <param name="pressedElementOpenedPopup">Defines if the pressed element opened a popup or not.</param>
public KeyTipPressedResult(bool pressedElementAquiredFocus, bool pressedElementOpenedPopup)
{
this.PressedElementAquiredFocus = pressedElementAquiredFocus;
this.PressedElementOpenedPopup = pressedElementOpenedPopup;
}
/// <summary>
/// Defines if the pressed element aquired focus or not.
/// </summary>
public bool PressedElementAquiredFocus { get; }
/// <summary>
/// Defines if the pressed element opened a popup or not.
/// </summary>
public bool PressedElementOpenedPopup { get; }
}
}

View File

@@ -0,0 +1,196 @@
// ReSharper disable once CheckNamespace
namespace Fluent
{
using System;
using System.ComponentModel;
using System.Linq;
using Fluent.Converters;
/// <summary>
/// Class to map from <see cref="RibbonGroupBoxState"/> to <see cref="RibbonControlSize"/>
/// </summary>
[TypeConverter(typeof(SizeDefinitionConverter))]
public struct RibbonControlSizeDefinition : IEquatable<RibbonControlSizeDefinition>
{
private const int MaxSizeDefinitionParts = 3;
/// <summary>
/// Creates a new instance
/// </summary>
public RibbonControlSizeDefinition(RibbonControlSize large, RibbonControlSize middle, RibbonControlSize small)
: this()
{
this.Large = large;
this.Middle = middle;
this.Small = small;
}
/// <summary>
/// Creates a new instance
/// </summary>
public RibbonControlSizeDefinition(string sizeDefinition)
: this()
{
if (string.IsNullOrEmpty(sizeDefinition))
{
this.Large = RibbonControlSize.Large;
this.Middle = RibbonControlSize.Large;
this.Small = RibbonControlSize.Large;
return;
}
var splitted = sizeDefinition.Split(new[] { ' ', ',', ';', '-', '>' }, MaxSizeDefinitionParts, StringSplitOptions.RemoveEmptyEntries).ToList();
if (splitted.Count == 0)
{
this.Large = RibbonControlSize.Large;
this.Middle = RibbonControlSize.Large;
this.Small = RibbonControlSize.Large;
return;
}
// Ensure that we got three sizes
for (var i = splitted.Count; i < MaxSizeDefinitionParts; i++)
{
splitted.Add(splitted[splitted.Count - 1]);
}
this.Large = ToRibbonControlSize(splitted[0]);
this.Middle = ToRibbonControlSize(splitted[1]);
this.Small = ToRibbonControlSize(splitted[2]);
}
/// <summary>
/// Gets or sets the value for large group sizes
/// </summary>
public RibbonControlSize Large { get; set; }
/// <summary>
/// Gets or sets the value for middle group sizes
/// </summary>
public RibbonControlSize Middle { get; set; }
/// <summary>
/// Gets or sets the value for small group sizes
/// </summary>
public RibbonControlSize Small { get; set; }
/// <summary>
/// Converts from <see cref="string"/> to <see cref="RibbonControlSizeDefinition"/>
/// </summary>
public static implicit operator RibbonControlSizeDefinition(string sizeDefinition)
{
return new RibbonControlSizeDefinition(sizeDefinition);
}
/// <summary>
/// Converts from <see cref="RibbonControlSizeDefinition"/> to <see cref="string"/>
/// </summary>
public static implicit operator string(RibbonControlSizeDefinition sizeDefinition)
{
return sizeDefinition.ToString();
}
/// <summary>
/// Converts from <see cref="string"/> to <see cref="RibbonControlSize"/>
/// </summary>
public static RibbonControlSize ToRibbonControlSize(string ribbonControlSize)
{
return Enum.TryParse(ribbonControlSize, true, out RibbonControlSize result)
? result
: RibbonControlSize.Large;
}
/// <summary>
/// Gets the appropriate <see cref="RibbonControlSize"/> from <see cref="Large"/>, <see cref="Middle"/> or <see cref="Small"/> depending on <paramref name="ribbonGroupBoxState"/>
/// </summary>
public RibbonControlSize GetSize(RibbonGroupBoxState ribbonGroupBoxState)
{
switch (ribbonGroupBoxState)
{
case RibbonGroupBoxState.Large:
return this.Large;
case RibbonGroupBoxState.Middle:
return this.Middle;
case RibbonGroupBoxState.Small:
return this.Small;
case RibbonGroupBoxState.Collapsed:
case RibbonGroupBoxState.QuickAccess:
return this.Large;
default:
return RibbonControlSize.Large;
}
}
#region Overrides of ValueType
/// <inheritdoc />
public override bool Equals(object obj)
{
if (obj is null)
{
return false;
}
return obj is RibbonControlSizeDefinition definition
&& this.Equals(definition);
}
#region Equality members
/// <inheritdoc />
public bool Equals(RibbonControlSizeDefinition other)
{
return this.Large == other.Large && this.Middle == other.Middle && this.Small == other.Small;
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
var hashCode = (int)this.Large;
hashCode = (hashCode * 397) ^ (int)this.Middle;
hashCode = (hashCode * 397) ^ (int)this.Small;
return hashCode;
}
}
/// <summary>Determines whether the specified object instances are considered equal.</summary>
/// <param name="left">The first object to compare. </param>
/// <param name="right">The second object to compare. </param>
/// <returns>true if the objects are considered equal; otherwise, false. If both <paramref name="left" /> and <paramref name="right" /> are null, the method returns true.</returns>
public static bool operator ==(RibbonControlSizeDefinition left, RibbonControlSizeDefinition right)
{
return left.Equals(right);
}
/// <summary>Determines whether the specified object instances are not considered equal.</summary>
/// <param name="left">The first object to compare.</param>
/// <param name="right">The second object to compare.</param>
/// <returns>true if the objects are not considered equal; otherwise, false. If both <paramref name="left" /> and <paramref name="right" /> are null, the method returns false.</returns>
public static bool operator !=(RibbonControlSizeDefinition left, RibbonControlSizeDefinition right)
{
return !left.Equals(right);
}
#endregion
#endregion
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>
/// A string that represents the current object.
/// </returns>
public override string ToString()
{
return $"{this.Large} {this.Middle} {this.Small}";
}
}
}

View File

@@ -0,0 +1,326 @@
// ReSharper disable once CheckNamespace
namespace Fluent
{
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.IsolatedStorage;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
/// <summary>
/// Handles loading and saving the state of a <see cref="Ribbon"/> from/to a <see cref="MemoryStream"/>, for temporary storage, and from/to <see cref="IsolatedStorage"/>, for persistent storage.
/// </summary>
public class RibbonStateStorage : IRibbonStateStorage
{
private static readonly MD5 md5Hasher = MD5.Create();
private readonly Ribbon ribbon;
// Name of the isolated storage file
private string isolatedStorageFileName;
private readonly Stream memoryStream;
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="ribbon">The <see cref="Ribbon"/> of which the state should be stored.</param>
public RibbonStateStorage(Ribbon ribbon)
{
this.ribbon = ribbon;
this.memoryStream = new MemoryStream();
}
/// <summary>
/// Finalizes an instance of the <see cref="RibbonStateStorage"/> class.
/// </summary>
~RibbonStateStorage()
{
this.Dispose(false);
}
/// <summary>
/// Gets whether this object already got disposed.
/// </summary>
protected bool Disposed { get; private set; }
/// <inheritdoc />
public bool IsLoading { get; private set; }
/// <inheritdoc />
public bool IsLoaded { get; private set; }
/// <summary>
/// Gets name of the isolated storage file
/// </summary>
protected string IsolatedStorageFileName
{
get
{
if (this.isolatedStorageFileName != null)
{
return this.isolatedStorageFileName;
}
var stringForHash = string.Empty;
var window = Window.GetWindow(this.ribbon);
if (window != null)
{
stringForHash += "." + window.GetType().FullName;
if (string.IsNullOrEmpty(window.Name) == false
&& window.Name.Trim().Length > 0)
{
stringForHash += "." + window.Name;
}
}
if (string.IsNullOrEmpty(this.ribbon.Name) == false
&& this.ribbon.Name.Trim().Length > 0)
{
stringForHash += "." + this.ribbon.Name;
}
this.isolatedStorageFileName = "Fluent.Ribbon.State." + BitConverter.ToInt32(md5Hasher.ComputeHash(Encoding.Default.GetBytes(stringForHash)), 0).ToString("X");
return this.isolatedStorageFileName;
}
}
/// <inheritdoc />
public virtual void SaveTemporary()
{
this.memoryStream.Position = 0;
this.Save(this.memoryStream);
}
/// <inheritdoc />
public virtual void Save()
{
// Check whether automatic save is valid now
if (this.ribbon.AutomaticStateManagement == false)
{
Debug.WriteLine("State not saved to isolated storage. Because automatic state management is disabled.");
return;
}
if (this.IsLoaded == false)
{
Debug.WriteLine("State not saved to isolated storage. Because state was not loaded before.");
return;
}
try
{
var storage = GetIsolatedStorageFile();
using (var stream = new IsolatedStorageFileStream(this.IsolatedStorageFileName, FileMode.Create, FileAccess.Write, storage))
{
this.Save(stream);
}
}
catch (Exception ex)
{
Trace.WriteLine($"Error while trying to save Ribbon state. Error: {ex}");
}
}
/// <summary>
/// Saves state to <paramref name="stream"/>.
/// </summary>
/// <param name="stream">Stream</param>
protected virtual void Save(Stream stream)
{
// Don't save or load state in design mode
if (DesignerProperties.GetIsInDesignMode(this.ribbon))
{
return;
}
var builder = this.CreateStateData();
var writer = new StreamWriter(stream);
writer.Write(builder.ToString());
writer.Flush();
}
/// <summary>
/// Create the serialized state data which should be saved later.
/// </summary>
/// <returns><see cref="StringBuilder"/> which contains the serialized state data.</returns>
protected virtual StringBuilder CreateStateData()
{
var builder = new StringBuilder();
// Save Ribbon State
builder.Append(this.ribbon.IsMinimized.ToString(CultureInfo.InvariantCulture));
builder.Append(',');
builder.Append(this.ribbon.ShowQuickAccessToolBarAboveRibbon.ToString(CultureInfo.InvariantCulture));
return builder;
}
/// <inheritdoc />
public virtual void LoadTemporary()
{
this.memoryStream.Position = 0;
this.Load(this.memoryStream);
}
/// <inheritdoc />
public virtual void Load()
{
// Don't save or load state in design mode
if (DesignerProperties.GetIsInDesignMode(this.ribbon))
{
Debug.WriteLine("State not loaded from isolated storage. Because we are in design mode.");
this.IsLoaded = true;
return;
}
if (this.ribbon.AutomaticStateManagement == false)
{
Debug.WriteLine("State not loaded from isolated storage. Because automatic state management is disabled.");
this.IsLoaded = true;
return;
}
try
{
var storage = GetIsolatedStorageFile();
if (IsolatedStorageFileExists(storage, this.IsolatedStorageFileName))
{
using (var stream = new IsolatedStorageFileStream(this.IsolatedStorageFileName, FileMode.Open, FileAccess.Read, storage))
{
this.Load(stream);
// Copy loaded state to MemoryStream for temporary storage.
// Temporary storage is used for style changes etc. so we can apply the current state again.
stream.Position = 0;
this.memoryStream.Position = 0;
stream.CopyTo(this.memoryStream);
}
}
}
catch (Exception ex)
{
Trace.WriteLine($"Error while trying to load Ribbon state. Error: {ex}");
}
this.IsLoaded = true;
}
/// <summary>
/// Loads state from <paramref name="stream"/>.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to load the state from.</param>
protected virtual void Load(Stream stream)
{
this.IsLoading = true;
try
{
this.LoadStateCore(stream);
}
finally
{
this.IsLoading = false;
}
}
/// <summary>
/// Loads state from <paramref name="stream"/>.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to load the state from.</param>
protected virtual void LoadStateCore(Stream stream)
{
var reader = new StreamReader(stream);
var data = reader.ReadToEnd();
this.LoadState(data);
}
/// <summary>
/// Loads state from <paramref name="data"/>.
/// </summary>
/// <param name="data">The <see cref="string"/> to load the state from.</param>
protected virtual void LoadState(string data)
{
// Load Ribbon State
var ribbonProperties = data.Split(',');
this.ribbon.IsMinimized = bool.Parse(ribbonProperties[0]);
this.ribbon.ShowQuickAccessToolBarAboveRibbon = bool.Parse(ribbonProperties[1]);
}
/// <summary>
/// Determines whether the given file exists in the given storage
/// </summary>
protected static bool IsolatedStorageFileExists(IsolatedStorageFile storage, string fileName)
{
var files = storage.GetFileNames(fileName);
return files.Length != 0;
}
/// <summary>
/// Get this <see cref="IsolatedStorageFile"/> which should be used to store the current state.
/// </summary>
/// <returns><see cref="IsolatedStorageFile.GetUserStoreForDomain"/> or <see cref="IsolatedStorageFile.GetUserStoreForAssembly"/> if <see cref="IsolatedStorageFile.GetUserStoreForDomain"/> threw an exception.</returns>
protected static IsolatedStorageFile GetIsolatedStorageFile()
{
try
{
return IsolatedStorageFile.GetUserStoreForDomain();
}
catch
{
return IsolatedStorageFile.GetUserStoreForAssembly();
}
}
/// <summary>
/// Resets saved state.
/// </summary>
public virtual void Reset()
{
var storage = GetIsolatedStorageFile();
foreach (var filename in storage.GetFileNames("*Fluent.Ribbon.State*"))
{
storage.DeleteFile(filename);
}
}
/// <inheritdoc />
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <param name="disposing">Defines whether managed resources should also be freed.</param>
protected virtual void Dispose(bool disposing)
{
if (this.Disposed)
{
return;
}
if (disposing)
{
this.memoryStream.Dispose();
}
this.Disposed = true;
}
}
}