This commit is contained in:
2024-02-23 06:55:00 -05:00
commit 277fc69c11
12 changed files with 1073 additions and 0 deletions

162
Animation/FeedManager.cs Normal file
View File

@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Threading;
using Ticker.Models;
using Ticker.Helper;
namespace Ticker.Animation
{
public class FeedManager
{
private static int FEED_DEFAULT_INTERVAL_MINUTES=1;
private static String FEED_CACHE_FILE_NAME="feedcache.txt";
private readonly Dictionary<string, FeedItem> feedItems = new Dictionary<string, FeedItem>();
private DispatcherTimer dispatcherTimer = new DispatcherTimer();
public event EventHandler<ItemEventArgs<FeedItem>> NewFeedItem;
public delegate List<FeedItem> GetFeedItemsEventHandler();
public event GetFeedItemsEventHandler FeedSourceEventHandler;
private bool stop = false;
private bool isInit=true;
public FeedManager()
{
ReadCache();
dispatcherTimer.Tick += DispatcherTimer_Tick;
dispatcherTimer.Interval = new TimeSpan(0, FEED_DEFAULT_INTERVAL_MINUTES, 0);
}
public virtual void Dispose()
{
Stop();
}
public void MarkFeedAsRead(FeedItem item)
{
if(!feedItems.ContainsKey(item.Guid))return;
feedItems[item.Guid].Read = true;
WriteCache(item);
}
public int GetFeedItemCount()
{
int count=0;
lock(feedItems)
{
if(null==feedItems||0==feedItems.Count)return count;
List<FeedItem> feedItemsList=new List<FeedItem>(feedItems.Values);
count=(from FeedItem feedItem in feedItemsList where feedItem.Read==false select feedItem).Count();
}
return count;
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
Go();
}
public int FeedIntervalMinutes
{
get{return dispatcherTimer.Interval.Minutes;}
set{dispatcherTimer.Interval=new TimeSpan(0,value,0);}
}
public void Start()
{
stop = false;
Go();
dispatcherTimer.Start();
}
public void Stop()
{
if(stop)return;
if(null!=dispatcherTimer)dispatcherTimer.Stop();
stop = true;
}
private void Go()
{
if(stop)return;
if(isInit)
{
isInit=false;
AddOpeningBanner();
}
ThreadPool.QueueUserWorkItem(LoadFeed,null);
}
// ***************************************************************************************************************************************************
// *************************************************************************** F E E D C A C H E ****************************************************
// ***************************************************************************************************************************************************
private void ReadCache()
{
lock(feedItems)
{
feedItems.Clear();
List<FeedItem> cacheItems=FeedSerializer.Deserialize(FEED_CACHE_FILE_NAME);
foreach(FeedItem feedItem in cacheItems)
{
if(!feedItems.ContainsKey(feedItem.Guid))feedItems.Add(feedItem.Guid,feedItem);
}
}
}
private void WriteCache(FeedItem feedItem)
{
lock(feedItems)
{
List<FeedItem> cacheItems=new List<FeedItem>(feedItems.Values);
FeedSerializer.Serialize(FEED_CACHE_FILE_NAME,DateTime.Today,feedItem);
}
}
// ***************************************************************************************************************************************************
private void AddOpeningBanner()
{
FeedItem feedItem=new FeedItem();
DateTime today=DateTime.Now;
feedItem.Description="TradeBlotter";
feedItem.Title=today.ToLongDateString()+" "+today.ToLongTimeString();
feedItem.Guid=today.ToLongTimeString();
feedItem.Link="https://www.time.gov/";
feedItem.Source="";
feedItem.PubDate=today.ToLongDateString();
feedItems.Add(feedItem.Guid,feedItem);
if (NewFeedItem != null) NewFeedItem(this, new ItemEventArgs<FeedItem>(feedItem));
}
private void LoadFeed(object state)
{
try
{
List<FeedItem> newFeedItems=null;
if(null==FeedSourceEventHandler)return;
newFeedItems=FeedSourceEventHandler();
if(null==newFeedItems||0==newFeedItems.Count)return;
foreach (FeedItem item in newFeedItems)
{
if (stop) return;
if (feedItems.ContainsKey(item.Guid)) continue;
item.Title = Clean(item.Title).Replace("\r","").Replace("\n","");
item.Description = Clean(item.Description);
lock (feedItems)
{
feedItems.Add(item.Guid, item);
}
if (NewFeedItem != null) NewFeedItem(this, new ItemEventArgs<FeedItem>(item));
}
}
catch(Exception)
{
}
}
private static string Clean(string input)
{
string output = input.Trim();
output = output.Replace("&#39;", "'");
output = output.Replace("&amp;", "&");
output = output.Replace("&quot;", "\"");
output = output.Replace("&nbsp;", " ");
output = RemoveHTMLTags(output);
return output;
}
private static string RemoveHTMLTags(string text)
{
string regularExpressionString = "<.+?>";
Regex r = new Regex(regularExpressionString, RegexOptions.Singleline);
return r.Replace(text, "");
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
namespace Ticker.Animation
{
public class ItemEventArgs<T> : EventArgs
{
public ItemEventArgs(T item)
{
Item = item;
}
public T Item { get; private set; }
}
}

View File

@@ -0,0 +1,14 @@
using System.Windows;
using System.Windows.Media.Animation;
namespace Ticker.Animation
{
public class TaggedDoubleAnimation : DoubleAnimation
{
public FrameworkElement TargetElement { get; set; }
protected override Freezable CreateInstanceCore()
{
return new TaggedDoubleAnimation{TargetElement = TargetElement};
}
}
}

95
Animation/Ticker.cs Normal file
View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace Ticker.Animation
{
public class Ticker<T> where T : FrameworkElement
{
private static int DISPLAY_TIMER_SPEED_SECONDS=40; // This is the speed used to traverse the display from right to left
private static int DISPLAY_TIMER_INTERVAL_MILLISECONDS=1000; // This is the interval of time between displaying successive items
private readonly DispatcherTimer displayTimer=new DispatcherTimer(DispatcherPriority.Normal); // new DispatcherTimer();
public EventHandler<ItemEventArgs<T>> ItemDisplayed;
public Ticker(Panel container)
{
SeperatorSize = 25;
Container = container;
Container.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
Container.Arrange(new Rect(Container.DesiredSize));
Speed = new TimeSpan(0, 0, 0, DISPLAY_TIMER_SPEED_SECONDS);
Items = new Queue<T>();
displayTimer.Tick += displayTimer_Tick;
displayTimer.Start();
IsRunning = true;
}
public virtual void Dispose()
{
Stop();
}
public bool IsRunning { get; set; }
public void Stop()
{
if(!IsRunning)return;
if(null!=displayTimer)displayTimer.Stop();
IsRunning = false;
}
public void Start()
{
if(IsRunning)return;
if(null==displayTimer)return;
displayTimer.Start();
displayTimer.Interval = new TimeSpan(0,0,0,0,DISPLAY_TIMER_INTERVAL_MILLISECONDS);
IsRunning = true;
}
public double SeperatorSize { get; set; }
public TimeSpan Speed { get; set; }
public Queue<T> Items { get; set; }
public Panel Container { get; private set; }
private void displayTimer_Tick(object sender, EventArgs e)
{
DisplayNextItem();
}
private void DisplayNextItem()
{
if (Items.Count == 0)return;
T item = Items.Dequeue();
Container.Children.Add(item);
AnimateMove(item);
if (ItemDisplayed != null) ItemDisplayed(this, new ItemEventArgs<T>(item));
}
private void AnimateMove(FrameworkElement e)
{
e.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
e.Arrange(new Rect(e.DesiredSize));
double from = Container.ActualWidth;
double to = -500;
int unitsPerSec = Convert.ToInt32(Math.Abs(from - to)/Speed.TotalSeconds);
int nextFire = Convert.ToInt32((e.ActualWidth + SeperatorSize)/unitsPerSec);
displayTimer.Stop();
displayTimer.Interval = new TimeSpan(0, 0, nextFire);
// displayTimer.Dispatcher.Thread.Priority=ThreadPriority.AboveNormal;
displayTimer.Start();
TaggedDoubleAnimation animation = new TaggedDoubleAnimation();
animation.From = from;
animation.To = to;
animation.Duration = new Duration(Speed);
animation.TargetElement = e;
animation.Completed += ani_Completed;
TranslateTransform translateTransform = new TranslateTransform();
e.RenderTransform = translateTransform;
translateTransform.BeginAnimation(TranslateTransform.XProperty, animation, HandoffBehavior.Compose);
}
private void ani_Completed(object sender, EventArgs e)
{
Clock clock = (Clock) sender;
TaggedDoubleAnimation animation = (TaggedDoubleAnimation) clock.Timeline;
FrameworkElement element = animation.TargetElement;
Container.Children.Remove(element);
}
}
}