Вкладки в стиле Google Chrome на стекле в Delphi

Я переупаковал реализацию Дастина Кэмпбелла, чтобы немного облегчить ее расширение для разных типов событий, когда универсальные обработчики не используются. Я полагаю, что это может быть кому-то полезно.

Кредиты:
Г-н. Первоначальная реализация Кэмпбелла
Очень удобная функция приведения делегатов Эд Болла, ссылка может быть найдена в источнике

Обработчик и пара перегрузок, EventHander < E> и PropertyChangedEventHandler:


///  Basic weak event management. 
/// 
///  Weak allow objects to be garbage collected without having to unsubscribe
///  
///  Taken with some minor variations from:
///  http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
///  
///  use as class.theEvent +=new EventHandler(instance_handler).MakeWeak((e) => class.theEvent -= e);
///  MakeWeak extension methods take an delegate to unsubscribe the handler from the event
/// 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;

namespace utils {

 /// 
 /// Delegate of an unsubscribe delegate
 /// 
 public delegate void UnregisterDelegate(H eventHandler) where H : class;

 /// 
 /// A handler for an event that doesn't store a reference to the source
 /// handler must be a instance method
 /// 
 /// type of calling object
 /// type of event args
 /// type of event handler
 public class WeakEventHandlerGeneric
  where T : class
  where E : EventArgs 
  where H : class {

  private delegate void OpenEventHandler(T @this, object sender, E e);

  private delegate void LocalHandler(object sender, E e);

  private WeakReference m_TargetRef;
  private OpenEventHandler m_OpenHandler;
  private H m_Handler;
  private UnregisterDelegate m_Unregister;

  public WeakEventHandlerGeneric(H eventHandler, UnregisterDelegate unregister) {
   m_TargetRef = new WeakReference((eventHandler as Delegate).Target);
   m_OpenHandler = (OpenEventHandler)Delegate.CreateDelegate(typeof(OpenEventHandler), null, (eventHandler as Delegate).Method);
   m_Handler = CastDelegate(new LocalHandler(Invoke));
   m_Unregister = unregister;
  }

  private void Invoke(object sender, E e) {
   T target = (T)m_TargetRef.Target;

   if (target != null)
    m_OpenHandler.Invoke(target, sender, e);
   else if (m_Unregister != null) {
    m_Unregister(m_Handler);
    m_Unregister = null;
   }
  }

  /// 
  /// Gets the handler.
  /// 
  public H Handler {
   get { return m_Handler; }
  }

  /// 
  /// Performs an implicit conversion from  to .
  /// 
  /// The weh.
  /// The result of the conversion.
  public static implicit operator H(WeakEventHandlerGeneric weh) {
   return weh.Handler;
  }

  /// 
  /// Casts the delegate.
  /// Taken from
  /// http://jacobcarpenters.blogspot.com/2006/06/cast-delegate.html
  /// 
  /// The source.
  /// 
  public static H CastDelegate(Delegate source) {
   if (source == null) return null;

   Delegate[] delegates = source.GetInvocationList();
   if (delegates.Length == 1)
    return Delegate.CreateDelegate(typeof(H), delegates[0].Target, delegates[0].Method) as H;

   for (int i = 0; i < delegates.Length; i++)
    delegates[i] = Delegate.CreateDelegate(typeof(H), delegates[i].Target, delegates[i].Method);

   return Delegate.Combine(delegates) as H;
  }
 }

 #region Weak Generic EventHandler handler

 /// 
 /// An interface for a weak event handler
 /// 
 /// 
 public interface IWeakEventHandler where E : EventArgs {
  EventHandler Handler { get; }
 }

 /// 
 /// A handler for an event that doesn't store a reference to the source
 /// handler must be a instance method
 /// 
 /// 
 /// 
 public class WeakEventHandler : WeakEventHandlerGeneric>, IWeakEventHandler
  where T : class
  where E : EventArgs {

  public WeakEventHandler(EventHandler eventHandler, UnregisterDelegate> unregister) 
   : base(eventHandler, unregister) { }
 }

 #endregion

 #region Weak PropertyChangedEvent handler

 /// 
 /// An interface for a weak event handler
 /// 
 /// 
 public interface IWeakPropertyChangedEventHandler {
  PropertyChangedEventHandler Handler { get; }
 }

 /// 
 /// A handler for an event that doesn't store a reference to the source
 /// handler must be a instance method
 /// 
 /// 
 /// 
 public class WeakPropertyChangeHandler : WeakEventHandlerGeneric, IWeakPropertyChangedEventHandler
  where T : class {

  public WeakPropertyChangeHandler(PropertyChangedEventHandler eventHandler, UnregisterDelegate unregister) 
   : base(eventHandler, unregister) {}
 }

 #endregion

 /// 
 /// Utilities for the weak event method
 /// 
 public static class WeakEventExtensions {

  private static void CheckArgs(Delegate eventHandler, Delegate unregister) {
   if (eventHandler == null) throw new ArgumentNullException("eventHandler");
   if (eventHandler.Method.IsStatic || eventHandler.Target == null) throw new ArgumentException("Only instance methods are supported.", "eventHandler");
  }

  private static object GetWeakHandler(Type generalType, Type[] genericTypes, Type[] constructorArgTypes, object[] constructorArgs) {
   var wehType = generalType.MakeGenericType(genericTypes);
   var wehConstructor = wehType.GetConstructor(constructorArgTypes);
   return wehConstructor.Invoke(constructorArgs);
  }

  /// 
  /// Makes a property change handler weak
  /// 
  /// 
  /// The event handler.
  /// The unregister.
  /// 
  public static PropertyChangedEventHandler MakeWeak(this PropertyChangedEventHandler eventHandler, UnregisterDelegate unregister) {
   CheckArgs(eventHandler, unregister);

   var generalType = typeof (WeakPropertyChangeHandler<>);
   var genericTypes = new[] {eventHandler.Method.DeclaringType};
   var constructorTypes = new[] { typeof(PropertyChangedEventHandler), typeof(UnregisterDelegate) };
   var constructorArgs = new object[] {eventHandler, unregister};

   return ((IWeakPropertyChangedEventHandler) GetWeakHandler(generalType, genericTypes, constructorTypes, constructorArgs)).Handler;
  }

  /// 
  /// Makes a generic handler weak
  /// 
  /// 
  /// The event handler.
  /// The unregister.
  /// 
  public static EventHandler MakeWeak(this EventHandler eventHandler, UnregisterDelegate> unregister) where E : EventArgs {
   CheckArgs(eventHandler, unregister);

   var generalType = typeof(WeakEventHandler<,>);
   var genericTypes = new[] { eventHandler.Method.DeclaringType, typeof(E) };
   var constructorTypes = new[] { typeof(EventHandler), typeof(UnregisterDelegate>) };
   var constructorArgs = new object[] { eventHandler, unregister };

   return ((IWeakEventHandler)GetWeakHandler(generalType, genericTypes, constructorTypes, constructorArgs)).Handler;
  }
 }
}

Модульные тесты:


using System.ComponentModel;
using NUnit.Framework;
using System.Collections.Generic;
using System;

namespace utils.Tests {
 [TestFixture]
 public class WeakEventTests {

  #region setup/teardown

  [TestFixtureSetUp]
  public void SetUp() {
   testScenarios.Add(SetupTestGeneric);
   testScenarios.Add(SetupTestPropChange);
  }

  [TestFixtureTearDown]
  public void TearDown() {

  }

  #endregion

  #region tests

  private List> testScenarios = new List>();

  private IEventSource source;
  private WeakReference sourceRef;

  private IEventConsumer consumer;
  private WeakReference consumerRef;

  private IEventConsumer consumer2;
  private WeakReference consumerRef2;

  [Test]
  public void ConsumerSourceTest() {
   foreach(var a in testScenarios) {
    a(false);
    ConsumerSourceTestMethod();
   }
  }

  private void ConsumerSourceTestMethod() {
   Assert.IsFalse(consumer.eventSet);
   source.Fire();
   Assert.IsTrue(consumer.eventSet);
  }

  [Test]
  public void ConsumerLinkTest() {
   foreach (var a in testScenarios) {
    a(false);
    ConsumerLinkTestMethod();
   }
  }

  private void ConsumerLinkTestMethod() {
   consumer = null;
   GC.Collect();
   Assert.IsFalse(consumerRef.IsAlive);
   Assert.IsTrue(source.InvocationCount == 1);
   source.Fire();
   Assert.IsTrue(source.InvocationCount == 0);
  }

  [Test]
  public void ConsumerLinkTestDouble() {
   foreach (var a in testScenarios) {
    a(true);
    ConsumerLinkTestDoubleMethod();
   }
  }

  private void ConsumerLinkTestDoubleMethod() {
   consumer = null;
   GC.Collect();
   Assert.IsFalse(consumerRef.IsAlive);
   Assert.IsTrue(source.InvocationCount == 2);
   source.Fire();
   Assert.IsTrue(source.InvocationCount == 1);
   consumer2 = null;
   GC.Collect();
   Assert.IsFalse(consumerRef2.IsAlive);
   Assert.IsTrue(source.InvocationCount == 1);
   source.Fire();
   Assert.IsTrue(source.InvocationCount == 0);
  }

  [Test]
  public void ConsumerLinkTestMultiple() {
   foreach (var a in testScenarios) {
    a(true);
    ConsumerLinkTestMultipleMethod();
   }
  }

  private void ConsumerLinkTestMultipleMethod() {
   consumer = null;
   consumer2 = null;
   GC.Collect();
   Assert.IsFalse(consumerRef.IsAlive);
   Assert.IsFalse(consumerRef2.IsAlive);
   Assert.IsTrue(source.InvocationCount == 2);
   source.Fire();
   Assert.IsTrue(source.InvocationCount == 0);
  }

  [Test]
  public void SourceLinkTest() {
   foreach (var a in testScenarios) {
    a(false);
    SourceLinkTestMethod();
   }
  }

  private void SourceLinkTestMethod() {
   source = null;
   GC.Collect();
   Assert.IsFalse(sourceRef.IsAlive);
  }

  [Test]
  public void SourceLinkTestMultiple() {
   SetupTestGeneric(true);
   foreach (var a in testScenarios) {
    a(true);
    SourceLinkTestMultipleMethod();
   }
  }

  private void SourceLinkTestMultipleMethod() {
   source = null;
   GC.Collect();
   Assert.IsFalse(sourceRef.IsAlive);
  }

  #endregion

  #region test helpers

  public void SetupTestGeneric(bool both) {
   source = new EventSourceGeneric();
   sourceRef = new WeakReference(source);

   consumer = new EventConsumerGeneric((EventSourceGeneric)source);
   consumerRef = new WeakReference(consumer);

   if (both) {
    consumer2 = new EventConsumerGeneric((EventSourceGeneric)source);
    consumerRef2 = new WeakReference(consumer2);
   }
  }

  public void SetupTestPropChange(bool both) {
   source = new EventSourcePropChange();
   sourceRef = new WeakReference(source);

   consumer = new EventConsumerPropChange((EventSourcePropChange)source);
   consumerRef = new WeakReference(consumer);

   if (both) {
    consumer2 = new EventConsumerPropChange((EventSourcePropChange)source);
    consumerRef2 = new WeakReference(consumer2);
   }
  }

  public interface IEventSource {
   int InvocationCount { get; }
   void Fire();
  }

  public class EventSourceGeneric : IEventSource {
   public event EventHandler theEvent;
   public int InvocationCount {
    get { return (theEvent != null)? theEvent.GetInvocationList().Length : 0; }
   }
   public void Fire() {
    if (theEvent != null) theEvent(this, EventArgs.Empty);
   }
  }

  public class EventSourcePropChange : IEventSource {
   public event PropertyChangedEventHandler theEvent;
   public int InvocationCount {
    get { return (theEvent != null) ? theEvent.GetInvocationList().Length : 0; }
   }
   public void Fire() {
    if (theEvent != null) theEvent(this, new PropertyChangedEventArgs(""));
   }
  }

  public interface IEventConsumer {
   bool eventSet { get; }
  }

  public class EventConsumerGeneric : IEventConsumer {
   public bool eventSet { get; private set; }
   public EventConsumerGeneric(EventSourceGeneric sourceGeneric) {
    sourceGeneric.theEvent +=new EventHandler(source_theEvent).MakeWeak((e) => sourceGeneric.theEvent -= e);
   }
   public void source_theEvent(object sender, EventArgs e) {
    eventSet = true;
   }
  }

  public class EventConsumerPropChange : IEventConsumer {
   public bool eventSet { get; private set; }
   public EventConsumerPropChange(EventSourcePropChange sourcePropChange) {
    sourcePropChange.theEvent += new PropertyChangedEventHandler(source_theEvent).MakeWeak((e) => sourcePropChange.theEvent -= e);
   }
   public void source_theEvent(object sender, PropertyChangedEventArgs e) {
    eventSet = true;
   }
  }

  #endregion
 }
}

16
задан Community 23 May 2017 в 10:31
поделиться

1 ответ

Я только что завершил довольно полную реализацию Chrome Tabs для Delphi.

enter image description here

Функции включают в себя:

  • Полностью настраиваемый внешний вид, включая градиенты, прозрачные пленки и настраиваемые формы вкладок
  • Вкладки могут быть окрашены в строке заголовка при использовании Aero
  • Работает на стекле Vista
  • Анимация движения вкладок
  • Эффекты переходного стиля вкладок (переход между цветами и альфа-уровнями)
  • Перетаскивание внутри контейнера и между контейнерами
  • Перетаскивание изображения отображает вкладку и любой TWinControl
  • Изменение размера смарт-вкладки при нажатии пользователем кнопки закрытия
  • Изменение размера вкладки жидкости с минимальной и максимальной вкладками размеры
  • Кнопка добавления вкладки может располагаться слева, справа или с плавающей справа
  • Полнофункциональная прокрутка, включая автоматическую прокрутку при перетаскивании
  • Отображение содержимого смарт-вкладки скрывает / отображает элементы в зависимости от на ширине вкладки
  • Владелец рисует любой элемент
  • Текст справа налево
  • Закрепленные вкладки
  • Счетчики вкладок - как визуализированные, так и растровые.
  • Модифицированные вкладки с анимированным свечением
  • Изображения вкладок и наложенные изображения
  • Наведите курсор на свечение
  • Много событий
  • Загрузите / сохраните внешний вид и ощущения и параметры для потоковой передачи / файла
  • Создание внешнего вида и параметров кода Delphi

Вы можете скачать исходный код и полную демонстрационную версию / редактор здесь: http: // code. google.com/p/delphi-chrome-tabs/

18
ответ дан 30 November 2019 в 17:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: