高中时候写的文章,现在搬过来。
一、关于 GarnitureControl
1. GarnitureControl 的加载与获取
查看 GS.Terminal.GarnitureControl.ControlFinder,注意到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class ControlFinder : IDisposable { [ImportMany(typeof(IControl))] public IControl[] Views; public bool Find() { try { AggregateCatalog aggregateCatalog = new AggregateCatalog(); DirectoryCatalog item = new DirectoryCatalog(AddonActivator.AddonContext.Addon.Location + "\\Controls", "*.dll"); aggregateCatalog.Catalogs.Add(item); new CompositionContainer(aggregateCatalog, new ExportProvider[0]).ComposeParts(new object[] { this }); return true; } catch (Exception errorException) { AddonActivator.AddonContext.Logger.Error("查找Control出错", errorException); } return false; } }
|
基本操作,没什么好说的,使用 System.ComponentModel.Composition 反射动态地加载程序集中指定类型(实现了 IControl 接口)的类罢了。
看到服务类。注意,这一段比较重要!虽然说只是获取一个 GarnitureControl 对象,但是涉及到一个 Action,其他地方可能用到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| namespace GS.Terminal.GarnitureControl { public class Service {
public UserControl FindControlByKey(string key, ref Action<object> handle) { GarnitureControl garnitureControl = GarnitureControl.Controls.FirstOrDefault((GarnitureControl ss) => ss.ControlKey == key); if (garnitureControl == null) { return null; } UserControl controlEntity = garnitureControl.ControlEntity; IControl @object = (IControl)controlEntity; handle = new Action<object>(@object.setData); return controlEntity; } } }
|
2. GarnitureControl 的有关操作
关于如何添加 GarnitureControl 不是这里讨论的重点。这里主要介绍如何操纵已经存在的 GarnitureControl。
以跑马灯为例。看到 /GS.Terminal.GarnitureControl/Controls/CommonControls.dll - CommonControls.BannerMessage。我们主要关心它的 setData() 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| [ControlInfo("跑马灯信息展示", "BannerMessage")]
public class BannerMessage : UserControl, IControl, IComponentConnector { public void setData(object data) { bool flag = data != null; if (flag) { bool flag2 = data.ToString().StartsWith("Command.Add"); if (flag2) { this.MsgList.Add(data.ToString().Substring(11)); } bool flag3 = data.ToString().StartsWith("Command.Remove"); if (flag3) { this.MsgList.Remove(data.ToString().Substring(14)); } bool flag4 = this.MsgList.Count == 1; if (flag4) { this.canvas1.Visibility = Visibility.Visible; this.PlayIndex = 0; this.CeaterAnimation(this.msg); } bool flag5 = this.MsgList.Count == 0; if (flag5) { this.canvas1.Visibility = Visibility.Collapsed; this.msg.Text = ""; } } } }
|
再看看 GS.Terminal.SmartBoard.Logic.Garitures 中如何初始化 BannerMessage?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| internal static void InitBannerMessageControl() { double width; double left; double top; if (SystemParameters.PrimaryScreenWidth == 1080.0) { width = 1020.0; left = 30.0; top = 1598.0; } else { width = 1700.0; left = 110.0; top = 840.0; } GarnitureControl garnitureControl = new GarnitureControl(); Action<object> controlHandle = null; UserControl userControl = Utilites.FindControlByKey("BannerMessage", ref controlHandle); garnitureControl.ControlHandle = controlHandle; userControl.Width = width; garnitureControl.ControlEntity = userControl; garnitureControl.ID = Utilites.AddGarnitureControl(userControl, top, left); garnitureControl.Key = "BannerMessage"; GaritureCore.GarnitureControlList.Add(garnitureControl); Program.TerminalStateManagement.StateChanged += BannerMessageControl.TerminalStateManagement_StateChanged; }
|
这时候,看到 GS.Terminal.SmartBoard.Logic.Garitures.BannerMessageControl,它实际上是对 CommonControls.BannerMessage.setData() 的一系列封装。
以添加跑马灯消息为例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| internal static void AddBannerMsg(string Msg) { if (BannerMessageControl.banner == null) { BannerMessageControl.banner = GaritureCore.GarnitureControlList.FirstOrDefault((GarnitureControl ss) => ss.Key == "BannerMessage"); } if (BannerMessageControl.banner != null) { Program.AddonContext.Logger.Debug("AddBannerMsg", null); DispatcherHelper.CheckBeginInvokeOnUI(delegate { BannerMessageControl.banner.ControlHandle("Command.Add" + Msg); }); } }
|
同时,这里再提出一种不需要修改班牌 dll 的自定义跑马灯的方法。
可以通过 GarnitureControl 插件服务类中的 FindControlByKey() 获取 ControlHandle 的 Action<object> 实例的引用(注:这时候也会返回一个 UserControl 类型的实例的引用,但是那个实例的引用是无法操纵的,这涉及到 UI 线程安全等等,不是讨论的重点),然后就可以控制 GarnitureControl 的行为了。
二、关于 VisualBlock
0. 如何打开 localData.db
要理解以下代码,需要查看 localData.db 的内容。
考虑使用 SqliteStudio,加密算法 System.Data.Sqlite: RC4,密码 123。
1. VisualBlock 的加载与初始化
查看 GS.Terminal.SmartBoard.Logic.VisualBlockCore.Startup。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
| internal void LoadVisualTemplate() { try { using (Session session = Program.ObjectSpace.GetSession(ChannelMode.Multiton)) { this.Templates.Clear(); this._ThemeName = string.Empty; object dblocker = this._dblocker; lock (dblocker) { Local_Visual_Publish local_Visual_Publish = session.Query<Local_Visual_Publish>() .FirstOrDefault((Local_Visual_Publish ss) => ss.TerminalID == Program.MachineId);
if (local_Visual_Publish != null) { this._ThemeName = local_Visual_Publish.ThemeName;
List<Local_VisualTemplate> templates = (from ss in local_Visual_Publish.Templates orderby ss.SortIndex select ss).ToList<Local_VisualTemplate>();
int num = 1; Func<Local_VisualBlock, VisualBlockItem> <>9__2; foreach (Local_VisualTemplate local_VisualTemplate in templates) {
List<BlockTemplate> templates2 = this.Templates;
BlockTemplate blockTemplate = new BlockTemplate();
blockTemplate.TemplateName = local_VisualTemplate.TemplateName;
blockTemplate.DisplayName = local_VisualTemplate.DisplayName;
blockTemplate.Index = num++;
blockTemplate.TemplateType = BlockTemplateType.Theme;
IEnumerable<Local_VisualBlock> source = local_VisualTemplate.VisualBlocks.ToList<Local_VisualBlock>();
Func<Local_VisualBlock, VisualBlockItem> selector;
if ((selector = <>9__2) == null) { selector = (<>9__2 = delegate(Local_VisualBlock b) { BaseBlock blockEntity = null; DispatcherHelper.RunAsync(delegate { blockEntity = this.GetBlock(b.BlockTypeName);
blockEntity.DataSource = (b.DataSource.StartsWith("http") ? b.DataSource : (Program.WebPath + "/" + b.DataSource)); blockEntity.Init(Program.AddonContext); }).Wait(); if (!string.IsNullOrEmpty(b.NavTemplateName)) { blockEntity.NavPageIndex = templates.FindIndex((Local_VisualTemplate ss) => ss.TemplateName == b.NavTemplateName) + 1; } return new VisualBlockItem { Id = Guid.Parse(b.BlockID),
BlockComponent = b.BlockComponent,
BlockTypeName = b.BlockTypeName, DataSource = b.DataSource, NavTemplateName = b.NavTemplateName,
Height = b.Height, Width = b.Width, X = b.X, Y = b.Y, DataContext = blockEntity }; }); } blockTemplate.Blocks = source.Select(selector).ToList<VisualBlockItem>(); templates2.Add(blockTemplate); } } } DataUpdate.Instance.RebuildUpdateList(); session.Disconnect(); } } catch (Exception errorException) { Program.AddonContext.Logger.Error("加载主题模版异常", errorException); } }
|
2. VisualBlock 如何获取数据?
在刚刚的加载过程中,RebuildUpdateList() 方法创建了一个列表,其内容为依据实例化了的各种 VisualBlock 创建的一系列 UpdateBlock,用于管理 VisualBlock 数据的获取和更新。
不过注意,UpdateBlock 类只是描述了一个数据结构,实际的逻辑并不在此处。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class UpdateBlock { public UpdateBlock(VisualBlockItem item) { this.BlockEntity = (IUpdate)item.DataContext; this.BlockId = item.Id; this.TypeName = item.BlockTypeName; this.WebRequester = new VisualBlockWebRequest(new Uri(Program.localSetting.GlobalConfig.WebPath + "/" + item.DataSource), string.Format("{0}_{1}.json", item.BlockTypeName, item.Id)); } ... }
|
下面展示的两个方法控制 Block 数据的更新。其中,UpdateAllBlock() 方法会在 GS.Terminal.SmartBoard.Logic 插件启动时被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
internal void UpdateAllBlock() { foreach (string typeName in this._updateBlocks.Keys) { this.UpdateBlockDataByTypeName(typeName); } }
internal void UpdateBlockDataByTypeName(string typeName) { List<UpdateBlock> list; if (this._updateBlocks.TryGetValue(typeName, out list)) { list.ForEach(delegate(UpdateBlock b) { if (!b.IsBusy) { try { b.IsBusy = true; if (b.WebRequester.UpdateData() == VisualBlockUpdateResult.Update) { b.BlockEntity.LoadLocalData(b.WebRequester.CacheFile);
} } finally { b.IsBusy = false; } } }); } }
|
这些逻辑就是实现班牌滑动页面上控件更新的全部了。
仿照这样的逻辑,我们可以写一个人为创建自定义 VisualTemplate 的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| public void RewriteVisualTemplate(string template_name, string display_name, int template_overwrite_index, List<VisualBlockItem> items) { BlockTemplate blockTemplate = new BlockTemplate(); blockTemplate.TemplateName = template_name; blockTemplate.DisplayName = display_name; blockTemplate.Index = template_overwrite_index; blockTemplate.TemplateType = BlockTemplateType.Theme;
blockTemplate.Blocks = items;
blockTemplate.Previous = Utilites.ViewModelLocator.MainPage.TemplateList[template_overwrite_index - 1]; blockTemplate.Next = Utilites.ViewModelLocator.MainPage.TemplateList[template_overwrite_index + 1];
DispatcherHelper.RunAsync( () => { Utilites.ViewModelLocator.MainPage.TemplateList[template_overwrite_index] = blockTemplate; } ); }
public static VisualBlockItem GetBlock(string block_name, string json_filename, string component, int width, int height, int x, int y, string data_source = "", string nav_template_name = "") {
IBlockService firstOrDefaultService = Program.AddonContext. GetFirstOrDefaultService<IBlockService>("GS.Terminal.VisualBlock"); BaseBlock block = firstOrDefaultService.GetBlock(block_name); block.Init(Program.AddonContext);
IUpdate update = (IUpdate)block; update.LoadLocalData(media_json_filename);
return new VisualBlockItem { Id = Guid.NewGuid(), BlockComponent = component, BlockTypeName = ((IBlock)block).TypeName, DataSource = data_source, NavTemplateName = nav_template_name, Width = width, Height = height, X = x, Y = y, DataContext = (BaseBlock)update } }
|
3. 一种可能的不需要修改 dll 的添加自定义 VisualBlock 的方法
另外,这里再提出一种不需要修改 dll 的方法。
使用 GS.Terminal.LogicShell 的 IViewHelperService 或者 GalaSoft.MvvmLight 的 SimpleIoc 获取相关的 ViewModel,再进行操作。