WPF技巧 n問

問一:Popup控件Placement屬性設置固定值後,在不一樣的電腦可能有不一樣行爲的問題

Popup.Placement屬性受SystemParameters.MenuDropAlignment(該值指示彈出菜單相對於相應菜單項是左對齊仍是右對齊。)屬性的影響,你能夠這麼設置:html

if (SystemParameters.MenuDropAlignment)
{
	Popup.Placement = PlacementMode.Left;
}
else
{
	Popup.Placement = PlacementMode.Right;
}

問二:將ListBox的ItemsSource,Binding到List<String>爲何ListBox裏面不是String列表而是ListBoxItem列表呢?

ItemsControl有兩個虛方法:app

protected override DependencyObject GetContainerForItemOverride()
{
      //return new CustomListItem();
}

protected override bool IsItemItsOwnContainerOverride(object item)
{
      //return item is CustomListItem;
}

顧名思義,這兩個方法一個是判斷有沒有子項容器有沒有被重寫,一個是返回新的子項。你能夠建立本身的ListBox,裏面容納本身的ListBoxItem,就像我上面那樣(解掉兩行註釋)。ide

問三:如何以管理員身份啓動應用程序?

在WPF項目添加應用程序清單文件app.manifest,找到以下所示的塊:函數

<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <!-- UAC 清單選項
            若是要更改 Windows 用戶賬戶控制級別,請用如下節點之一替換 
            requestedExecutionLevel 節點。

        <requestedExecutionLevel  level="requireAdministratorasInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />

            指定 requestedExecutionLevel 節點將會禁用文件和註冊表虛擬化。
            若是要利用文件和註冊表虛擬化實現向後 
            兼容性,則刪除 requestedExecutionLevel 節點。
        -->
        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
</requestedPrivileges>

將<requestedExecutionLevel level="asInvoker" uiAccess="false" />改成<requestedExecutionLevel level="requireAdministratorasInvoker" uiAccess="false" /> 便可,這裏還有其餘選項,不做贅述。性能

問四:應用程序啓動後如何檢查是否以管理員身份運行的?

		/// <summary>
		/// 檢查是不是管理員身份
		/// </summary>
		private void CheckAdministrator()
		{
			var wi = WindowsIdentity.GetCurrent();
			var wp = new WindowsPrincipal(wi);

			bool runAsAdmin = wp.IsInRole(WindowsBuiltInRole.Administrator);

			if (!runAsAdmin)
			{
				// It is not possible to launch a ClickOnce app as administrator directly,
				// so instead we launch the app as administrator in a new process.
				var processInfo = new ProcessStartInfo(Assembly.GetExecutingAssembly().CodeBase);

				// The following properties run the new process as administrator
				processInfo.UseShellExecute = true;
				processInfo.Verb = "runas";

				// Start the new process
				try
				{
					Process.Start(processInfo);
				}
				catch (Exception ex)
				{
					logger.Info(ex);
				}

				// Shut down the current process
				Environment.Exit(0);
			}
		}

在App構造函數或者App.OnStartup方法中調用。動畫

問五:WPF無邊框陰影窗口

看到這個問題你必定是以下設置吧:ui

<Window x:Class="WPFTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="350"
        AllowsTransparency="True"
        Background="Transparent"
        WindowStyle="None">
    <Grid Margin="10" Background="White">
        <Grid.Effect>
            <DropShadowEffect BlurRadius="10" ShadowDepth="0" />
        </Grid.Effect>
    </Grid>
</Window>

image

這樣設置能夠,可是窗體啓動慢、性能低、不能拖動、不能拖改尺寸、不跟隨系統窗口動畫(最大化最小化時動態拉伸窗口)、沒有窗體系統功能,另外你若這樣設置窗口,窗口裏面若是還有一個WebBrowser,那麼這個WebBrowser不會顯示。this

WPF4.5新增了System.Windows.Shell命名空間,這個命名空間已經集成在了x:命名空間下,因此你如今能夠這麼建立窗口:spa

<Window x:Class="WPFTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="350">
    <WindowChrome.WindowChrome>
        <WindowChrome CaptionHeight="30"
                      CornerRadius="0"
                      GlassFrameThickness="1"
                      NonClientFrameEdges="None"
                      ResizeBorderThickness="5"
                      UseAeroCaptionButtons="False" />
    </WindowChrome.WindowChrome>
    <Grid />
</Window>

image

關於WindowChrome類,查閱MSDN在線文檔:WindowChrome線程

問六:WPF路由命令的可用狀態不能及時刷新到UI控件的問題

路由命令的CanExecute事件並非不停的「投石問路」的。路由命令處於性能考慮在窗體是激活狀態的時候纔會不停地「投石問路」,但有時候也是會出現不觸發的狀況,這個時候你須要點擊一下窗體其餘地方,觸發一下焦點切換,纔會再次執行CanExecute事件。如何避免這個問題,而讓WPF始終不停地「投石問路」呢?

static ControlCommands()
{
         DispatcherTimer dt = new DispatcherTimer
         {
                Interval = TimeSpan.FromMilliseconds(500)
         };
         dt.Tick += (sender, e) =>
         {
		//強制 System.Windows.Input.CommandManager 引起 System.Windows.Input.CommandManager.RequerySuggested事件
                CommandManager.InvalidateRequerySuggested();
         };
         dt.Start();
}

使用Tick裏面的代碼強制路由命令執行CanExecute事件。

問七:屬性的更改通知、集合的更改通知

屬性的更改通知要實現INotifyPropertyChanged接口,如:

public class MyClass : INotifyPropertyChanged
{
	public event PropertyChangedEventHandler PropertyChanged;

	private void RaisePropertyChanged(string propertyname)
	{
		PropertyChangedEventHandler handle = this.PropertyChanged;
		if (!string.IsNullOrEmpty(propertyname) && handle != null)
		{
			handle.Invoke(this, new PropertyChangedEventArgs(propertyname));
		}
	}


	public string Property1
	{
		get { return this.Property1; }
		set
		{
			this.Property1 = value;
			this.RaisePropertyChanged("Property1");
		}
	}
}

這樣Property1屬性就具有了更改通知的功能(在WPFUI中綁定此屬性時)。

集合的更改通知要實現INotifyCollectionChanged接口,WPF自己只提供了一個這樣的接口就是System.Collections.ObjectModel.ObservableCollection<T>類,ItemsControl的ItemsSource屬性綁定到該類時,對該類進行的添加等操做會及時的更新到UI上,而不實現INotifyCollectionChanged接口的類則會報「綁定的集合與源集合不一致」異常。

問八:Binding到IEnumrable<T>的ItemsControl怎麼支持實時排序?

看代碼:

void View_Loaded(object sender, RoutedEventArgs e)
{
	if (this.tablesList.Items.CanSort)
	{
		//this.myitemsControl.Items.SortDescriptions.Add(new SortDescription("fiSortOrder", ListSortDirection.Ascending));
		//this.myitemsControl.Items.SortDescriptions.Add(new SortDescription("fsMTableName", ListSortDirection.Ascending));
	}
}

其中"fiSortOrder"、"fsMTableName"是你的自定義Model(MVVM)中的屬性字符串表示。

支持多列排序,升序逆序排序。

注意:排序性能較低,謹慎使用。

問9:帶事件的對象序列化問題

看代碼:

public class TestClass : INotifyPropertyChanged
{
	//[field: NonSerialized]
	public event PropertyChangedEventHandler PropertyChanged;
	protected void RaisePropertyChanged(string propertyname)
	{
		PropertyChangedEventHandler handle = this.PropertyChanged;
		if (!string.IsNullOrEmpty(propertyname) && handle != null)
		{
			handle.Invoke(this, new PropertyChangedEventArgs(propertyname));
		}
	}
}

將不須要的序列化的字段標記爲[NonSerialized],可是事件不是字段須要標記爲[field: NonSerialized] 如代碼第三行,解註釋便可。

問10:Brush、Color、String的相互轉換(本例爲摘抄)

using System.Windows.Media;

一、String轉換成Color

            Color color = (Color)ColorConverter.ConvertFromString(string);

二、String轉換成Brush

            BrushConverter brushConverter = new BrushConverter();
            Brush brush = (Brush)brushConverter.ConvertFromString(string);

三、Color轉換成Brush

            Brush brush = new SolidColorBrush(color));

四、Brush轉換成Color有兩種方法:

(1)先將Brush轉成string,再轉成Color。

            Color color= (Color)ColorConverter.ConvertFromString(brush.ToString());

(2)將Brush轉成SolidColorBrush,再取Color。

            Color color= ((SolidColorBrush)CadColor.Background).Color;

問11:捕獲應用程序內的全部異常,並保證程序不會中止工做

看代碼:

/// <summary>
/// App.xaml 的交互邏輯
/// </summary>
public partial class App : Application
{

	public App()
	{
		this.DispatcherUnhandledException += App_DispatcherUnhandledException;
		AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
	}


	void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
	{
		this.HandleException(e.ExceptionObject as Exception);
	}

	void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
	{
		e.Handled = true;
		this.HandleException(e.Exception);
	}

	private void HandleException(Exception exception)
	{

	}
}

處理UI線程中(即Dispatcher)的異常只須要註冊App.DispatcherUnhandledException事件便可,在其事件處理程序中e.Handled = true;應用程序便不會中止工做。

對於非UI線程的異常須要註冊AppDomain.CurrentDomain.UnhandledException事件,可是這個事件參數沒有e.Handled參數,也就是說,只要非UI線程發生了異常只是通知你,該退出仍是要退出,有一個辦法解決此問題:

配置文件添加:

<!--這裏的legacyUnhandledExceptionPolicy,若是enabled=1的話,用意是使用早期版本的異常處理策略。應用程序不退出-->
 <runtime>
    <legacyUnhandledExceptionPolicy enabled="1" />
 </runtime>

問12:獲取當前可執行程序的所在目錄

看到這個問題你的解決方法是什麼?使用Environment.CurrentDirectory?這一般狀況下不會有問題。對於該屬性,MSDN解釋爲:Environment.CurrentDirectory

使用這個屬性有一個問題須要注意:當你雙擊A.exe來啓動A.exe時該屬性正常工做。當你雙擊B.exe時在B.exe裏面Process.Start(「A.exe」)時,若A.exe和B.exe不在同一目錄下,在A.exe裏面使用該屬性時,會返回B.exe的路徑,解決方法爲:

/// <summary>
/// 當前可執行程序集的徹底路徑
/// </summary>
public static readonly string STARTPATH = Assembly
				.GetExecutingAssembly()
				.Location;

/// <summary>
/// 當前執行程序集的所在目錄
/// </summary>
public static readonly string CURRENTDIRECTORY = Path.GetDirectoryName(
			Assembly
			.GetExecutingAssembly()
			.Location);

問13:程序設置當前計算機輸入法(Winform、WPF)

WPF:

		void MainWindow_Loaded(object sender, RoutedEventArgs e)
		{
			//檢索本機安裝的輸入法
			foreach (CultureInfo item in InputLanguageManager.Current.AvailableInputLanguages)
			{
				MessageBox.Show(item.DisplayName);
			}

			//將本機輸入法設置爲如下輸入法之一
			foreach (CultureInfo item in InputLanguageManager.Current.AvailableInputLanguages)
			{
				if (item.DisplayName.Contains("百度") ||
					item.DisplayName.Contains("搜狗") ||
					item.DisplayName.Contains("QQ") ||
					item.DisplayName.Contains("谷歌"))
				{
					InputLanguageManager.Current.CurrentInputLanguage = item;
					return;
				}
			}

			//設置爲一箇中文輸入法
			foreach (CultureInfo item in InputLanguageManager.Current.AvailableInputLanguages)
			{
				if (item.Name.Equals("zh-CN", StringComparison.OrdinalIgnoreCase))
				{
					InputLanguageManager.Current.CurrentInputLanguage = item;
					return;
				}
			}

			//設置爲一個英文輸入法
			foreach (CultureInfo item in InputLanguageManager.Current.AvailableInputLanguages)
			{
				if (item.Name.Equals("en-US", StringComparison.OrdinalIgnoreCase))
				{
					InputLanguageManager.Current.CurrentInputLanguage = item;
					return;
				}
			}
		}

WinForm:

using Winform = System.Windows.Forms;

//設置四種輸入法之一
foreach (Winform.InputLanguage item in Winform.InputLanguage.InstalledInputLanguages)
{
	if (item.LayoutName.Contains("百度") ||
		item.LayoutName.Contains("搜狗") ||
		item.LayoutName.Contains("QQ") ||
		item.LayoutName.Contains("谷歌"))
	{
		Winform.InputLanguage.CurrentInputLanguage = item;
		return;
	}
}


//設置一箇中文輸入法
Winform.InputLanguage zh_cnlanguage = Winform.InputLanguage.FromCulture(CultureInfo.GetCultureInfo("zh-CN"));
if (zh_cnlanguage != null)
{
	Winform.InputLanguage.CurrentInputLanguage = zh_cnlanguage;
}


//設置一個英文輸入法
Winform.InputLanguage en_uslanguage = Winform.InputLanguage.FromCulture(CultureInfo.GetCultureInfo("en-US"));
if (en_uslanguage != null)
{
	Winform.InputLanguage.CurrentInputLanguage = en_uslanguage;
}

問14:可翻頁的ScrollViewer

自定義一個控件PageControl,將一個ScrollViewer放進去,再放四個Button,部好局。

控件代碼經過如下代碼找到放置的ScrollViewer和四個Button:

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			_scrollViewer = GetTemplateChild(PART_SCROLL) as ScrollViewer;
			if (_scrollViewer == null)
			{
				throw new ClientException(
					"模板加載異常,缺乏模板元素。",
					ExceptionKind.TemplatePartNull,
					ExceptionLevel.General);
			}

			_scrollViewer.ScrollChanged += _scrollViewer_ScrollChanged;
			_btTop = GetTemplateChild("TopBt") as Button;
			_btBottom = GetTemplateChild("BottomBt") as Button;
			_btRight = GetTemplateChild("RightBt") as Button;
			_btLeft = GetTemplateChild("LeftBt") as Button;

			//其實就是調用ScrollViewer相應方法
			if (_btTop != null)
			{
				_btTop.Click += (sender, e) => { this.PageUp(); };
			}
			if (_btBottom != null)
			{
				_btBottom.Click += (sender, e) => { this.PageDown(); };
			}
			if (_btRight != null)
			{
				_btRight.Click += (sender, e) => { this.PageRight(); };
			}
			if (_btLeft != null)
			{
				_btLeft.Click += (sender, e) => { this.PageLeft(); };
			}
		}

下面纔是本例真正要講述的東西:如何控制四個功能Button的狀態及其可見性。

狀態:

		void _scrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
		{
			switch (this.PageButtonLocation)
			{
				//豎直
				case PageButtonLocation.TopButtom:
				case PageButtonLocation.Top:
				case PageButtonLocation.Bottom:
					if (e.ExtentHeight <= this._scrollViewer.ViewportHeight)
					{
						this._btBottom.IsEnabled = false;
						this._btTop.IsEnabled = false;
						return;
					}
					if (e.VerticalOffset + this._scrollViewer.ViewportHeight == e.ExtentHeight)
					{
						this._btBottom.IsEnabled = false;
					}
					else
					{
						this._btBottom.IsEnabled = true;
					}
					if (e.VerticalOffset == 0)
					{
						this._btTop.IsEnabled = false;
					}
					else
					{
						this._btTop.IsEnabled = true;
					}
					break;

				//水平
				case PageButtonLocation.LeftRight:
				case PageButtonLocation.Left:
				case PageButtonLocation.Right:
					if (e.ExtentWidth <= this._scrollViewer.ViewportWidth)
					{
						this._btLeft.IsEnabled = false;
						this._btRight.IsEnabled = false;
						return;
					}
					if (e.HorizontalOffset + this._scrollViewer.ViewportWidth == e.ExtentWidth)
					{
						this._btRight.IsEnabled = false;
					}
					else
					{
						this._btRight.IsEnabled = true;
					}
					if (e.HorizontalOffset == 0)
					{
						this._btLeft.IsEnabled = false;
					}
					else
					{
						this._btLeft.IsEnabled = true;
					}
					break;
			}
		}
可見性(這個在XAML裏面比較容易控制):
<ControlTemplate.Triggers>
            <!--  控制Button可見性,成對按鈕都禁用時才隱藏  -->
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding ElementName=TopBt, Path=IsEnabled}" Value="False" />
                                <Condition Binding="{Binding ElementName=BottomBt, Path=IsEnabled}" Value="False" />
                            </MultiDataTrigger.Conditions>
                            <Setter TargetName="TopBt" Property="Visibility" Value="Collapsed" />
                            <Setter TargetName="BottomBt" Property="Visibility" Value="Collapsed" />
                        </MultiDataTrigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding ElementName=LeftBt, Path=IsEnabled}" Value="False" />
                                <Condition Binding="{Binding ElementName=RightBt, Path=IsEnabled}" Value="False" />
                            </MultiDataTrigger.Conditions>
                            <Setter TargetName="LeftBt" Property="Visibility" Value="Collapsed" />
                            <Setter TargetName="RightBt" Property="Visibility" Value="Collapsed" />
                        </MultiDataTrigger>
                    </ControlTemplate.Triggers>

問15:XAML命名空間

 

 

 

持續更新中…

相關文章
相關標籤/搜索