1、本地化支持
(1)重寫控件默認的依賴屬性LanguageProperty
FrameworkElement.LanguageProperty.OverrideMetadata( typeof(FrameworkElement), new FrameworkPropertyMetadata( XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
(2)在項目資源文件中添加Resources.resx,Resources.zh-CN.resx等等本地化字符串資源。
文件中依次輸入Key-Value鍵值對,key相同,Value中輸入本地化語言字符串,如下所示
(3)在界面中引用資源文件所在命名空間。如:
xmlns:resc="clr-namespace:WpfApplication.Resources"
(4)引用字符串資源。如 :
<Button Content="{x:Static resc:Resources.Open}"/>
2、主進程通知
在多文件支持和音視頻播放界面應用中,經常需要在外部通知主進程。比如您已經打開了酷狗音樂播放界面,在外部文件夾中,雙擊.mp3音樂文件,不會啟動第二個酷狗音樂播放界面,而是通知主窗口接收外部請求。下面給出一種實現方式:
首先聲明WPF開發的系統只有一個入口,那就是Application類,也就是App.xaml
(1)App.xaml中不需要StartupUri,在后台啟動主窗口。首先在App.xaml.cs中定義事件句柄,用於通知主窗口
public static EventWaitHandle ProgramStarted; private bool CreatedNew;
(2)App.xaml.cs重寫OnStartup,判斷是否需要新建主窗口
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, "MyStartEvent", out CreatedNew);
通過上面語句得到的CreatedNew值來決定是否需要創建主窗口,CreatedNew等於true時按一般情況聲明主窗口:
if (CreatedNew) { MainWindow mainWin = new MainWindow(); mainWin.Show(); }
(3)下面才是重點,在主窗口已經啟動的情況下,如何將外部的文件傳遞到主窗口去
因為前面已經定義了事件句柄ProgramStarted,我們首先需要將外部文件寫入到一個本地文件或者注冊表中(名其為Global),已經啟動的窗口進程得到通知后,去讀取新建App進程寫入的內容。
然后調用ProgramStarted.Set();Thread.Sleep(100);主窗口就可以得到通知。
(4)主窗口的線程池中需要注冊Wait請求:
MainWindow.xaml.cs構造函數中:
ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false);
事件回調處理函數:
/// <summary> /// 主進程在接收到其他進程通知后回調函數 /// </summary> /// <param name="state"></param> /// <param name="timeout"></param> private void OnProgramStarted(object state, bool timeout) { Thread thread = new Thread(new ThreadStart(new Action(() => { this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (System.Threading.ThreadStart)delegate() { //處理Global }); }))); //因為是線程池通知主進程,必須在單線程單元ApartmentState.STA執行 thread.SetApartmentState(ApartmentState.STA); thread.IsBackground = true; thread.Start(); }
注意上面代碼中使用了Dispatcher,只有WPF這樣使用,能夠訪問主窗口控件,因為主窗口控件所在的線程是主窗口,不能在新建的線程中調用,如果要使用,需要使用Dispatcher。
默認Dispatcher執行的環境也是ApartmentState.STA,所以上面的代碼可以簡單為(只能在WPF中可以這樣使用):
private void OnProgramStarted(object state, bool timeout) { this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (System.Threading.ThreadStart)delegate() { //處理Global }); }
3、兩種最常用異步編程
(1)DispatcherObject
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate() { // Simulate some work taking place. Thread.Sleep(TimeSpan.FromSeconds(5)); btnContent.Text = "Here is some new text."; } );
(2)BackgroundWorker
BackgroundWorker是WPF異步編程經常使用的類,使用方式比較正規,很容易學會。
BackgroundWorker bgw = new BackgroundWorker(); bgw.WorkerSupportsCancellation = true; bgw.WorkerReportsProgress = true; bgw.DoWork += new DoWorkEventHandler(bgw_DoWork); bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted); bgw.RunWorkerAsync(argument);
//bgw_DoWork不能直接使用主線程元素,只能通過e.Argument得到主線程傳過來的參數 private void bgw_DoWork(object sender, DoWorkEventArgs e) { string argument= e.Argument.ToString(); e.Result = new LongTimeFun(argument); } private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MyObject obj = e.Result as MyObject ; //這個時候才能使用主界面控件元素 } private MyObject LongTimeFun(string argument){ }
簡單形式(Lambda語法):
BackgroundWorker bgw = new BackgroundWorker(); bgw.WorkerSupportsCancellation = true; bgw.WorkerReportsProgress = true; bgw.DoWork += new DoWorkEventHandler((sender, e) => { e.Result = new LongTimeFun(argument); }); bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) => { MyObject obj = e.Result as MyObject ; });
(3)DispatcherObject和BackgroundWorker結合使用
我們知道BackgroundWorker的DoWork事件回調事件中不能使用界面元素,但是在WPF中可以結合使用DispatcherObject、BackgroundWorker達到目的,
這樣就不需要BackgroundWorker的RunWorkerCompleted回調事件了。(這個幾乎在書本和其它技術資料中沒有介紹,但是很多時候是很有用的)
BackgroundWorker bgw = new BackgroundWorker(); bgw.WorkerSupportsCancellation = true; bgw.WorkerReportsProgress = true; bgw.DoWork += new DoWorkEventHandler((sender, e) => { this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (System.Threading.ThreadStart)delegate() { //這樣DoWork回調事件中可以使用主界面控件元素 }); }); bgw.RunWorkerAsync();
4、滾動條內容設置可見
(1)使用FrameworkElement方法 BringIntoView
FrameworkElement.BringIntoView();
(2)ListBox
listbox.ScrollIntoView(listbox.Items[index]);
(3)Scrollviewer
scrollViewer.ScrollToVerticalOffset(VisualTreeHelper.GetOffset(VisualObject).Y);