WPF疑难杂症会诊
原文:WPF疑难杂症会诊
为什么图片像素是模糊的?
容器边框设为非整数时,其内容中的像素图片会产生模糊,即使设置SnapsToDevicePixels="True"也无效。
以下是范例代码:
Code
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <WrapPanel> <Border Margin="6" BorderThickness="1.5" BorderBrush="Red" HorizontalAlignment="Center" VerticalAlignment="Center"> <Image Width="146" Height="229" Stretch="None" Source="http://images.cnblogs.com/cnblogs_com/skyd/WPF_FLOWDOC_5.png%22/> </Border> <Border Margin="6" BorderThickness="1" BorderBrush="Red" HorizontalAlignment="Center" VerticalAlignment="Center"> <Image Width="146" Height="229" Stretch="None" Source="http://images.cnblogs.com/cnblogs_com/skyd/WPF_FLOWDOC_5.png%22/> </Border> </WrapPanel> </Page>
我建立了两个Border,其边框宽度分别为1.5和1,内容都是载入的同一个Png格式图片。
效果如下:
可以看到,1.5像素边框的Border内的图片是模糊的,而另一个是正常的。
分析:
推断这种情况是因为受布局的位置影响,图片被绘制到非整数像素位置,这时为正确且无锯齿显示,就必须羽化自身像素。
解决的办法就是调整相关布局,避免在布局中使用非整数数值。
矢量图形不会受此影响。
Gird布局无法自适应内容扩展了!
在这篇文章里我曾写过如何自动化布局:http://www.cnblogs.com/SkyD/archive/2008/08/02/1258555.html
让窗口尺寸自适应内容,然后让Grid也自适应内容,这是一件很惬意的事。
比如这样:
Code
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" SizeToContent="WidthAndHeight"> <Grid Margin="6" Width="200" ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Ellipse Grid.Row="0" Height="55" Fill="Blue"/> <Ellipse Grid.Row="1" Height="155" Fill="Red"/> <Ellipse Grid.Row="2" Height="255" Fill="Green"/> </Grid> </Window>
效果为:
这看起来很完美,但是如果你把每个圆形的高度增加100像素,问题就出来了:
可以看到,最后一个圆形没有完全显示,这似乎很不合逻辑。
分析:
产生这种问题,应该是和屏幕分辨率有关的,我的屏幕分辨率是1440*900,我猜想窗口可能会自作聪明地将自己的尺寸限制在一个看起来比较舒适的的区域中,从而使得其内容被裁减。
你可以通过将“<RowDefinition Height="*"/>”全部改为“<RowDefinition Height="Auto"/>”来解决这一问题,设为“Auto”后,Grid将强制将行高定为其内容所需的高度。
此前我们所设置的“*”的作用实际上是为内容自动分配剩余空间的高度,在剩余空间充足的情况下,它还会自动进行一些智能化的调整,使得呈现更为合理,而当剩余空间紧张时,它就不得不强行为一些大块头开刀了。
设为“Auto”后的效果:
怎么才能禁止内容撑大容器?
这似乎很简单,只要设置容器为固定尺寸就可以了。但是假如我们为了自动化布局而不能设置容器尺寸呢?或者,假如我们在定义一个通用的样式,我们根本不知道目标容器的尺寸呢?
例如下面这段代码:
Code
<WrapPanel> <ListBox HorizontalContentAlignment="Stretch" Margin="8" Width="120" Height="220" BorderBrush="Blue" BorderThickness="3"> <ListBoxItem>1111111111</ListBoxItem> <ListBoxItem>22222222222222222222</ListBoxItem> <ListBoxItem>333333333333333333333333333333333333</ListBoxItem> <ListBoxItem>44444444444444444444444444444444444444444</ListBoxItem> </ListBox> <ListBox HorizontalContentAlignment="Stretch" Margin="8" Width="180" Height="220" BorderBrush="Green" BorderThickness="3"> <ListBoxItem>1111111111</ListBoxItem> <ListBoxItem>22222222222222222222</ListBoxItem> <ListBoxItem>333333333333333333333333333333333333</ListBoxItem> <ListBoxItem>44444444444444444444444444444444444444444</ListBoxItem> </ListBox> </WrapPanel>
有两个不同宽度的ListBox,我们设置其部分内容必定会超出其显示范围,现在的效果:
可以看到横向滚动条出现了。
我们定义一个模板来更直观的看一下显示效果:
Code
<Style TargetType="ListBoxItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <Border> <Border.Background> <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> <GradientStop Offset="0" Color="OrangeRed"/> <GradientStop Offset="1" Color="Brown"/> </LinearGradientBrush> </Border.Background> <TextBlock Text="{TemplateBinding Content}"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
我们将ListBoxItem的内容绑定给一个TextBlock,然后为其外围的Border设置了横向渐变背景。
显示为:
现在可以看到,渐变背景的长度参差不齐。
即使你为TextBlock设置换行,也不会有任何效果,它们仍然会撑开Border。
分析:
子元素无法获知外围容器的尺寸,从而无法进行常规的控制,目前我只研究出一个偏方用于解决这一问题,就是在内容与外围容器之间加入一层Canvas。
这个例子中就是为Border外加上Canvas,并把Border的背景挪给Canvas,完整代码如下:
Code
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Page.Resources> <Style TargetType="ListBoxItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <Canvas Height="16"> <Canvas.Background> <LinearGradientBrush StartPoint="0,0" EndPoint="1,0"> <GradientStop Offset="0" Color="OrangeRed"/> <GradientStop Offset="1" Color="Brown"/> </LinearGradientBrush> </Canvas.Background> <Border> <TextBlock Text="{TemplateBinding Content}"/> </Border> </Canvas> </ControlTemplate> </Setter.Value> </Setter> </Style> </Page.Resources> <WrapPanel> <ListBox Width="120" Height="220" Margin="8" BorderBrush="Blue" BorderThickness="3" HorizontalContentAlignment="Stretch"> <ListBoxItem>1111111111 </ListBoxItem> <ListBoxItem>22222222222222222222 </ListBoxItem> <ListBoxItem>333333333333333333333333333333333333 </ListBoxItem> <ListBoxItem>44444444444444444444444444444444444444444 </ListBoxItem> </ListBox> <ListBox Width="180" Height="220" Margin="8" BorderBrush="Green" BorderThickness="3" HorizontalContentAlignment="Stretch"> <ListBoxItem>1111111111 </ListBoxItem> <ListBoxItem>22222222222222222222 </ListBoxItem> <ListBoxItem>333333333333333333333333333333333333 </ListBoxItem> <ListBoxItem>44444444444444444444444444444444444444444 </ListBoxItem> </ListBox> </WrapPanel> </Page>
显示效果:
可以看到,横向滚动条滚出去了,渐变背景也是整齐的了。
这个偏方并不完美,假如我们希望文字能够自动换行,或是在即将超出边框的地方显示省略号,都无法办到。
期待高手能提出更好的解决方案。
怎么弄出CheckListBox来?
见这篇文章:http://www.cnblogs.com/SkyD/archive/2008/07/23/1249950.html
如何在多选列表中实现右键菜单?
就是这种效果:
这看起来很简单,但是当你做的时候可能会比较头疼。
你会发现,当你用鼠标右键在上面单击的时候,右键菜单正确弹出,但是同时你鼠标所处位置的列表项的选取状态也被改变了,这时如果用户没有注意到,就可能会产生很严重的误操作,比如将本不该删除的项目删除了。
分析:
WPF中,鼠标右键也可进行选取列表项的操作,为了禁用这一功能,我们必须在ListBox中拦截下这个事件,让ListBoxItem无法获知此事件的发生。
这是由noorbakhsh提供的方法:
在ListBox中加入事件处理:PreviewMouseRightButtonDown="listBox1_PreviewMouseRightButtonDown"
拦截事件:
Code
private void listBox1_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { e.Handled = true; }
这样就成功的屏蔽了右键选取功能。
怎么让多选列表中所有项的选择状态反转?
这也是个看似简单的问题,但是在WPF是不同以往的情况。
首先这种方法是不行的:
Code
ListBox lb = new ListBox(); foreach (ListBoxItem lbi in lb.Items) { lbi.IsSelected = !lbi.IsSelected; }
Items里装的不是ListBoxItem,而是源数据,比如string、FileInfo等等,任意类型。
需要使用这种方法设置:
Code
public static void ReSelect(ListBox l) { for (int i = 0; i < l.Items.Count; i++) { var f = l.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem; f.IsSelected = !f.IsSelected; } }
但是这还会引发一个问题,就是在列表内项目过多时,它会引发异常,而ListBox自带的ListBox.SelectAll() 和 ListBox.UnselectAll()方法则不会引发异常。
分析:
这种异常源自WPF的动态加载特性,这可以很大程度的节约载入时间和系统资源,但是如果你直接操作未加载到当前视图内的项,就会引发异常。
Marco Zhou 给出了两种解决方法:
1.禁用动态加载:<ListBox VirtualizingStackPanel.IsVirtualizing="False"/>
2.将列表项绑定到自定义类时,为自定义类增加一个用于控制是否选中的属性,并通过样式设定,将其绑定到列表项的IsSelected属性上:
Code
<StackPanel> <ListBox ItemsSource="{Binding}" x:Name="listBox" Width="200" Height="50"> <ListBox.ItemContainerStyle> <Style TargetType="{x:Type ListBoxItem}"> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/> <Setter Property="Content" Value="{Binding Data}"/> </Style> </ListBox.ItemContainerStyle> </ListBox> <Button Margin="5" Width="120" Height="30" x:Name="button"/> </StackPanel>
Code
public class DataObject : INotifyPropertyChanged { public DataObject(string data) { this.data = data; } private string data; public string Data { get { return data; } set { data = value; NotifyChange("Data"); } } private Boolean isSelected = false; public bool IsSelected { get { return isSelected; } set { isSelected = value; NotifyChange("IsSelected"); } } private void NotifyChange(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler PropertyChanged; } public partial class ListBoxDeselectDemo : Window { public ListBoxDeselectDemo() { InitializeComponent(); listBox.SelectionMode = SelectionMode.Multiple; List<DataObject> result = (from i in Enumerable.Range(1, 20) select new DataObject("Item" + i.ToString())).ToList<DataObject>(); this.DataContext = result; button.Click += delegate { for (int i = 0; i < result.Count; i++) { result[i].IsSelected = !result[i].IsSelected; } listBox.Focus(); }; } }
如何在多种样式之间共享相同的部分?
需要使用样式的继承属性,这是由RredCat提供的解决办法。
请参看这篇文章:http://www.cnblogs.com/SkyD/archive/2008/08/09/1264294.html
在此我列出了一些我在编写MailMail期间遇到的一些问题和解决办法,算是抛砖引玉,其中有些是使用偏方解决的,期待高手能提供出最佳的解决方法。
WPF疑难杂症会诊的更多相关文章
- WPF疑难杂症之一(3D场景)
原文:WPF疑难杂症之一(3D场景) 最近2个月一直在学习WPF,在实际的开发中遇到下面一个3D场景有关的问题,我先给出问题代码:首先是在资源中定义了一个3D变换组:<Window x:Clas ...
- WPF疑难杂症之二(全屏幕窗口)
原文:WPF疑难杂症之二(全屏幕窗口) 近日的学习中遇到一个非常奇怪的问题:用XAML文件创建了一个全屏幕窗口,然后,在窗口中建立了一个非常简单的动画.一切都在我的掌控之中,实现非常的顺利. WPF中 ...
- 意外地解决了一个WPF布局问题
原文:意外地解决了一个WPF布局问题 今天做了一个小测试,意外地将之前的一个困扰解决了,原问题见<WPF疑难杂症会诊>中的“怎么才能禁止内容撑大容器?” 以前我是在外侧嵌套Canvas容器 ...
- C# (事件触发)回调函数,完美处理各类疑难杂症!
每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客! 废话说多了...... 嘿嘿:本篇标题为:C# (事件触发)回调函数,完美处理各类疑难杂症.个人理解如下:事件触发也就是触 ...
- MVVM在WPF中应用(1)
在软件行业浸润了这么多年,第一次在MES的工厂里从事软件开发. 在这里的感觉就是安静.宽松,比在那些专门以软件为主的企业中轻松自在.在这里的第一个项目是关于数据的导入和导出,还有数据的比较这些功能. ...
- WPF 程序无法触摸操作?我们一起来找原因和解决方法!
WPF 自诞生以来就带着微软先生的傲慢.微软说 WPF 支持触摸,于是 WPF 就真的支持触摸了.对,我说的是"支持触摸",那种摸上去能点能动的:偶尔还能带点儿多指的炫酷效果.但是 ...
- 在WPF中使用依赖注入的方式创建视图
在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...
- MVVM框架从WPF移植到UWP遇到的问题和解决方法
MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...
- MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息
MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...
随机推荐
- Logistic Regression(逻辑回归)(一)基本原理
(整理自AndrewNG的课件,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 虽然叫做“回归”,但是这个算法是用来解决分类问题的.回归与分类的区 ...
- 通过无线连接的方式来做 Appium 自动化
感谢TesterHome里各种大牛,提出的宝贵思路,我这里只是将他们的想法综合了一下,试出来的成果,谢谢大家分享你们的智慧. 简单说下背景: 由于公司要测试APP 产品的耗电问题,我们采取的办法很lo ...
- PreTranslateMessage和TranslateMessage区别
PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,绝大多数本窗口的消息都要通过这里,比较常用,当需要在MFC之前处理某些消息时,常常要在这里添加代码. ...
- StackOverFlow的2016统计
http://stackoverflow.com/research/developer-survey-2016
- 显示出eclipse文件层次
看到图片中右边那个倒三角型符号没, 点一下,弹出个菜单,选package presentation->hierarachial 文件目录结构 flat 是包结构
- Swift - AnyObject与Any的区别
1,AnyObject :代表任何class类型的对象实例. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Man{ } class Woman{ ...
- 使用TWebBrowser时存在内存泄漏问题的解决方案(使用SetProcessWorkingSetSize函数,或者修改OleCtrls.pas源码解决问题)
用TWebBrower不断打开多个网页,多某些版本的操作系统上运行一段时间后,发现占用系统内存达几百M,直到关闭程序后,占用的内存才能释放. 这个问题在网有很多讨论,比较多人的建议办法是用SetPro ...
- MFC-消息分派
前言 由于工作需要,这几天学了一点MFC,在AFX里看到很多熟悉的东西,如类型信息,序列化,窗口封装和消息分派.几乎每个界面库都必须提供这些基础服务,但提供的手法却千差万别.MFC大量地借用了宏,映射 ...
- mysql 查询优化案例
mysql> explain SELECT c.`sn` clientSn,asm.`clientManagerSn`,pry.`productSn`,1 TYPE,pr.`capitalBal ...
- Swift - 自定义函数规则说明
1,无返回值的函数 1 2 3 func test(name:String){ } 2,返回一个返回值 1 2 3 func test(name:String) -> Bool{ r ...