原文: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疑难杂症会诊的更多相关文章

  1. WPF疑难杂症之一(3D场景)

    原文:WPF疑难杂症之一(3D场景) 最近2个月一直在学习WPF,在实际的开发中遇到下面一个3D场景有关的问题,我先给出问题代码:首先是在资源中定义了一个3D变换组:<Window x:Clas ...

  2. WPF疑难杂症之二(全屏幕窗口)

    原文:WPF疑难杂症之二(全屏幕窗口) 近日的学习中遇到一个非常奇怪的问题:用XAML文件创建了一个全屏幕窗口,然后,在窗口中建立了一个非常简单的动画.一切都在我的掌控之中,实现非常的顺利. WPF中 ...

  3. 意外地解决了一个WPF布局问题

    原文:意外地解决了一个WPF布局问题 今天做了一个小测试,意外地将之前的一个困扰解决了,原问题见<WPF疑难杂症会诊>中的“怎么才能禁止内容撑大容器?” 以前我是在外侧嵌套Canvas容器 ...

  4. C# (事件触发)回调函数,完美处理各类疑难杂症!

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客! 废话说多了...... 嘿嘿:本篇标题为:C#  (事件触发)回调函数,完美处理各类疑难杂症.个人理解如下:事件触发也就是触 ...

  5. MVVM在WPF中应用(1)

    在软件行业浸润了这么多年,第一次在MES的工厂里从事软件开发. 在这里的感觉就是安静.宽松,比在那些专门以软件为主的企业中轻松自在.在这里的第一个项目是关于数据的导入和导出,还有数据的比较这些功能. ...

  6. WPF 程序无法触摸操作?我们一起来找原因和解决方法!

    WPF 自诞生以来就带着微软先生的傲慢.微软说 WPF 支持触摸,于是 WPF 就真的支持触摸了.对,我说的是"支持触摸",那种摸上去能点能动的:偶尔还能带点儿多指的炫酷效果.但是 ...

  7. 在WPF中使用依赖注入的方式创建视图

    在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...

  8. MVVM框架从WPF移植到UWP遇到的问题和解决方法

    MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...

  9. MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息

    MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...

随机推荐

  1. swift-var/let定义变量和常量

    // Playground - noun: a place where people can play import UIKit //--------------------------------- ...

  2. deflate——过时的网页压缩格式,最好禁用[转]

    在设置GZip时,发现同时有个Deflate压缩设置,一开始并不了解Deflate压缩,于是便在启用GZip的同时,也启用了Deflate压缩.虽然同时设置GZip和Deflate压缩,并不影响网站的 ...

  3. CF卡技术详解——笔记

    知识太全面了,摘抄摘不完,还是粘过来加上注释和笔记吧. 重点以及断句用加粗,注释用红括号. 一.CF卡技术及规格 一.CF卡技术及规格 1.CF卡简史 随着数码产品的高速普及,近年来闪存卡也进入了高速 ...

  4. 奇怪的问题,InvalidateRect最后一个参数在XP下无效

    一直用的WIN2K系统,写的一个程序在本机正常,到XP系统的机器运行发现调整窗口大小时界面闪得厉害,程序比较大,而且这种闪烁还不好调试,因为单步调试没有闪烁效果,只能排除法找原因,最后以为找到原因了, ...

  5. Spring中的FactoryBean

    从SessionFactory说起: 在使用SSH集成开发的时候,我们有时候会在applicationContext.xml中配置Hibernate的信息,以下是配置SessionFactory的一段 ...

  6. ContentProvider简要

    1.什么是ContentProvider  数据库在Android其中是私有的,当然这些数据包含文件数据和数据库数据以及一些其它类型的数据.  不能将数据库设为WORLD_READABLE,每一个数据 ...

  7. 无边无状态栏窗口(使用GetWindowLongPtr设置GWL_EXSTYLE)

    通过SetWindowLongPtr来设置窗口样式 var NewStyle: Integer; begin Application.Initialize; Application.MainFormO ...

  8. NET单元测试的艺术

    NET单元测试的艺术 开篇:上一篇我们学习基本的单元测试基础知识和入门实例.但是,如果我们要测试的方法依赖于一个外部资源,如文件系统.数据库.Web服务或者其他难以控制的东西,那又该如何编写测试呢?为 ...

  9. 执行命令行并等待完成(使用WaitForSingleObject达到目的)

    function TDMDb.WaitExeFinish(const sCmdName: string):boolean; var StartupInfo: TStartupInfo; Process ...

  10. win7下怎样设置putty免用户名密码登陆

    putty是一款好用的远程登录linux服务器软件,但每次输入用户名密码毕竟有些烦人,这里教你免用户名密码登陆.   工具/原料 putty 方法/步骤   去百度下载putty,小巧易用,仅有0.5 ...