问一: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>
这样设置可以,但是窗体启动慢、性能低、不能拖动、不能拖改尺寸、不跟随系统窗口动画(最大化最小化时动态拉伸窗口)、没有窗体系统功能,另外你若这样设置窗口,窗口里面如果还有一个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>
关于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命名空间
持续更新中…