### 先去 CODE
可以通過引入並呼叫 `SetThreadExecutionState` 去達成防止電腦進入休眠及睡眠模式。
```c#
/**
* application main window
*/
public partial class MainWindow : Window {
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
[FlagsAttribute]
public enum EXECUTION_STATE : uint {
ES_AWAYMODE_REQUIRED = 0x00000040,
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001,
}
/**
* constructor
*/
public MainWindow() {
// initialize component
InitializeComponent();
// set startup event handler
App.Current.Startup += new StartupEventHandler((sender, e) => {
// keep screen awake
SetThreadExecutionState(EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
});
// set exit event handler
App.Current.Exit += new ExitEventHandler((sender, e) => {
// restore screen state
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
});
}
}
```
### 大阪
自從上次西藏之旅後,已經很久沒有出國旅行了。趁著這次復活節假期,終於有機會來到大阪。不過,近來新聞頻傳,甚至有所謂的預言家聲稱,2025年4月至8月日本可能發生「南海海槽大地震」,導致許多人對前往日本旅遊卻步。出發前,我心裡也有些忐忑,但轉念一想:「現在不去,難道要等到地震後才去嗎?」於是,最終還是決定按原計劃出發!
這次的旅程以大阪為中心,入住的是位於道頓堀河畔的**難波道頓堀假日酒店**。酒店地理位置極佳,無論是前往心齋橋、難波,或是道頓堀美食街都非常方便。唯一的缺點是周邊有不少牛郎店,十字路口常有年輕人在招攬客人,熱情邀請路人去他們的俱樂部飲酒作樂。不過,他們通常不會主動搭訕外國遊客,因此對觀光客的影響並不大。

### DAY 1 : 到著
這次搭乘的是國泰航空,早上八點多起飛,抵達關西機場時已是當地時間下午一點多。接著轉乘南海電鐵前往難波站,再步行至道頓堀,抵達酒店時已接近下午四點。由於一路上只吃了飛機餐,早已飢腸轆轆,於是立刻衝去附近的**一蘭拉麵**填飽肚子——畢竟到了晚上,排隊人潮可是相當驚人的!



飽餐一頓後,我們直奔日本橋電器街(俗稱「大阪的秋葉原」),想看看有沒有新推出的動漫周邊或有趣的玩意。結果,光是夾糖機就玩了將近一小時,戰利品是一大堆巧克力,心情大好!途中還經過一家扭蛋店,立刻開啟「代購模式」,一邊拍照一邊幫朋友挑選。沒想到,這一扭就是兩、三個小時,直到店家關燈打烊,我們才依依不捨地離開。


回到道頓堀時已將近晚上十點,許多店鋪都已打烊。原來不只香港,大阪的商店也關得挺早的!最後,我們隨意找了一家居酒屋解決晚餐。不過,日本居酒屋在晚上十點後通常會加收「座位費」(約350日圓/人),即使不點酒水也會計入帳單。雖然我們不喝酒,但既然來了,還是點了些壽司、炒飯等經典菜色,體驗當地的飲食文化。這一頓下來,總共花了近一萬日圓,但至少填飽了肚子。回到酒店後,疲憊不堪的我們立刻倒頭就睡,畢竟明天還有更多行程等著呢!






### 先去 CODE
下面是 view model
```c#
// 定義一個 private log message 的 ovservable collection (如果用普通 list 的話, wpf 會唔識 reactive update 個 UI)
private ObservableCollection<LogMessage> _logMessages = new ObservableCollection<LogMessage>();
// 再定義一個 public 的 Log messages 用來呼叫 OnPropertyChanged() 通知 wpf update 返個 UI
public ObservableCollection<LogMessage> LogMessages {
get => _logMessages;
set {
if (_logMessages != value) {
_logMessages = value;
OnPropertyChanged();
}
}
}
```
下面是 xaml
```xaml
<ListView ItemsSource="{Binding Path=LogMessages}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Grid.Row="1"
Grid.Column="0">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable"
Value="False" />
<Setter Property="IsHitTestVisible"
Value="False" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,10">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Title}"
Foreground="{Binding Path=Color}"
FontWeight="Bold"
TextWrapping="Wrap"
FontSize="14"
Grid.Row="0"
Grid.Column="0" />
<TextBlock Text="{Binding Path=Message}"
Foreground="{Binding Path=Color}"
TextWrapping="Wrap"
Grid.Row="1"
Grid.Column="0" />
<TextBlock Text="{Binding Path=LogAt}"
Foreground="#999999"
TextWrapping="Wrap"
Margin="0,5,0,0"
Grid.Row="2"
Grid.Column="0" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
```
這樣就可以輕鬆畫出 listview

### 情況
因為有時我們在按下按鈕時,背後 command 的工作並不是在 ui thread 上執行,所以 canExecute 沒有辦法即時回應畫出結果到 UI 上。
### 解決
在 Java 上可以 invalidate ui 去叫 ui component 重新 repaint 過個 UI component。
而 WPF 上都可以叫過差不多的手法去完成,不過是 CommandManager 專用的方法。
```c#
// run this to invalidate ui
CommandManager.InvalidateRequerySuggested();
```
### 出現問題
最近要為一個沒有 API 的平台制作 Automation,搵過好多唔同既方法,最後發現 stackoverflow 竟然係因最多人推介用 `PowerShell` 來解決,在先前係用過 "按鍵精靈" 同埋 "AutoIT" 的經驗,既然今次又要做 Automation 就不如用啲未用過既方法來開始。
### PowerShell
經過一陣子的研究發現,原來 `PowerShell` 都係需要引入 .dll 程式庫來實現一些原生的 window 功能,而今次我要實行的是模擬 Mouse 及 Keyboard 的動作,對 Web 介面進行 Automation。簡單的追可以通過 WinForms 去實行。
```ps1
// 引入 System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
```
然後可以通過直接寫入 C# 代碼到 PowerShell 中即時運行。
以下的代碼是引入了外部的 `user32.dll` 以實行對 Mouse 及 Keyboard 的模擬動作。
```ps1
Add-Type -TypeDefinition @"
using System;
using System.Threading;
using System.Runtime.InteropServices;
/**
* mouse class
*/
public class Mouse {
// mouse action
public const uint MOUSEEVENTF_LEFTDOWN = 0x02;
public const uint MOUSEEVENTF_LEFTUP = 0x04;
public const uint MOUSEEVENTF_RIGHTDOWN = 0x08;
public const uint MOUSEEVENTF_RIGHTUP = 0x10;
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT {
public int X;
public int Y;
}
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int x, int y);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
}
/**
* keyboard class
*/
public class Keyboard {
// keys
public const byte VK_CONTROL = 0x11;
public const byte VK_RETURN = 0x0D;
public const byte VK_A = 0x41;
public const byte VK_B = 0x42;
public const byte VK_C = 0x43;
public const byte VK_D = 0x44;
public const byte VK_E = 0x45;
public const byte VK_F = 0x46;
public const byte VK_G = 0x47;
public const byte VK_H = 0x48;
public const byte VK_I = 0x49;
public const byte VK_J = 0x4A;
public const byte VK_K = 0x4B;
public const byte VK_L = 0x4C;
public const byte VK_M = 0x4D;
public const byte VK_N = 0x4E;
public const byte VK_O = 0x4F;
public const byte VK_P = 0x50;
public const byte VK_Q = 0x51;
public const byte VK_R = 0x52;
public const byte VK_S = 0x53;
public const byte VK_T = 0x54;
public const byte VK_U = 0x55;
public const byte VK_V = 0x56;
public const byte VK_W = 0x57;
public const byte VK_X = 0x58;
public const byte VK_Y = 0x59;
public const byte VK_Z = 0x5A;
// actions
public const uint KEYEVENTF_KEYDOWN = 0x0000;
public const uint KEYEVENTF_KEYUP = 0x0002;
[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, IntPtr dwExtraInfo);
}
"@
```
當引入完上面的代碼後,可以在下面自己加入你想要運行的代碼去呼叫引入的代碼。
```ps1
// Mouse 左鍵按下
[Mouse]::mouse_event([Mouse]::MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
// Mouuse 左鍵升起
[Mouse]::mouse_event([Mouse]::MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
```
這樣就可以用 PowerShell 呼叫 C# 來逹成你想要的功能了,正因為呼叫的是 C#,所以是機乎所有的 Window 功能也可以通過 PowerShell 去運行,所以要運行 `.ps1` 的時候可能需要另外的權限。