WPF技巧 n问

问一:Popup控件Placement属性设置固定值后,在不同的电脑可能有不同行为的问题

Popup.Placement属性受SystemParameters.MenuDropAlignment(该值指示弹出菜单相对于相应菜单项是左对齐还是右对齐。)属性的影响,你可以这么设置:

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

问二:将ListBox的ItemsSource,Binding到List<String>为什么ListBox里面不是String列表而是ListBoxItem列表呢?

ItemsControl有两个虚方法:

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

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

顾名思义,这两个方法一个是判断有没有子项容器有没有被重写,一个是返回新的子项。你可以创建自己的ListBox,里面容纳自己的ListBoxItem,就像我上面那样(解掉两行注释)。

问三:如何以管理员身份启动应用程序?

在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无边框阴影窗口

看到这个问题你一定是如下设置吧:

<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不会显示。

WPF4.5新增了System.Windows.Shell命名空间,这个命名空间已经集成在了x:命名空间下,所以你现在可以这么创建窗口:

<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;

1、String转换成Color

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

2、String转换成Brush

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

3、Color转换成Brush

            Brush brush = new SolidColorBrush(color));

4、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命名空间

 

 

 

持续更新中…

转载于:https://www.cnblogs.com/Mr--Zhang/p/5661121.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值