WPF疑难杂症会诊

简介: 原文:WPF疑难杂症会诊      为什么图片像素是模糊的?   容器边框设为非整数时,其内容中的像素图片会产生模糊,即使设置SnapsToDevicePixels="True"也无效。
原文: WPF疑难杂症会诊

 

 

 

为什么图片像素是模糊的?

 

容器边框设为非整数时,其内容中的像素图片会产生模糊,即使设置SnapsToDevicePixels="True"也无效。

 

以下是范例代码:

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 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.51,内容都是载入的同一个Png格式图片。

 

效果如下:

 

可以看到,1.5像素边框的Border内的图片是模糊的,而另一个是正常的。

 

分析:

 

推断这种情况是因为受布局的位置影响,图片被绘制到非整数像素位置,这时为正确且无锯齿显示,就必须羽化自身像素。

 

解决的办法就是调整相关布局,避免在布局中使用非整数数值。

 

矢量图形不会受此影响。

 

 

Gird布局无法自适应内容扩展了!

 

在这篇文章里我曾写过如何自动化布局:http://www.cnblogs.com/SkyD/archive/2008/08/02/1258555.html

 

让窗口尺寸自适应内容,然后让Grid也自适应内容,这是一件很惬意的事。

 

比如这样:

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 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”后的效果:

 

 

 

 

怎么才能禁止内容撑大容器?

 

这似乎很简单,只要设置容器为固定尺寸就可以了。但是假如我们为了自动化布局而不能设置容器尺寸呢?或者,假如我们在定义一个通用的样式,我们根本不知道目标容器的尺寸呢?

 

例如下面这段代码:

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 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,我们设置其部分内容必定会超出其显示范围,现在的效果:

 

可以看到横向滚动条出现了。

 

我们定义一个模板来更直观的看一下显示效果:

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 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,完整代码如下:

 

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 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"

 

拦截事件:

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
        private void listBox1_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)

        {

            e.Handled 
= true;

        }

 

这样就成功的屏蔽了右键选取功能。

 

 

怎么让多选列表中所有项的选择状态反转?

 

这也是个看似简单的问题,但是在WPF是不同以往的情况。

 

首先这种方法是不行的:

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Code
ListBox lb = new ListBox();  

  

foreach (ListBoxItem lbi in lb.Items)  

{  

    lbi.IsSelected 
= !lbi.IsSelected;                 

}  

 

 

Items里装的不是ListBoxItem,而是源数据,比如stringFileInfo等等,任意类型。

 

需要使用这种方法设置:

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 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属性上:

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 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>

 

 

 

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif 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(
thisnew 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(120)

                                   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期间遇到的一些问题和解决办法,算是抛砖引玉,其中有些是使用偏方解决的,期待高手能提供出最佳的解决方法。

 

 

目录
相关文章
|
前端开发 C# 容器
|
11天前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
|
4月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
59 1
|
8月前
|
C# Windows
WPF技术之图形系列Polygon控件
WPF Polygon是Windows Presentation Foundation (WPF)框架中的一个标记元素,用于绘制多边形形状。它可以通过设置多个点的坐标来定义多边形的形状,可以绘制任意复杂度的多边形。
451 0
|
8月前
|
C# Windows
WPF技术之RichTextBox控件
WPF RichTextBox是Windows Presentation Foundation (WPF)中提供的一个强大的文本编辑控件,它可以显示富文本格式的文本,支持多种文本处理操作。
345 0
|
4月前
|
前端开发 C# 容器
浅谈WPF之控件拖拽与拖动
使用过office的visio软件画图的小伙伴都知道,画图软件分为两部分,左侧图形库,存放各种图标,右侧是一个画布,将左侧图形库的图标控件拖拽到右侧画布,就会生成一个新的控件,并且可以自由拖动。那如何在WPF程序中,实现类似的功能呢?今天就以一个简单的小例子,简述如何在WPF中实现控件的拖拽和拖动,仅供学习分享使用,如有不足之处,还请指正。
106 2
|
8月前
|
数据挖掘 数据处理 C#
WPF技术之DataGrid控件
WPF DataGrid是一种可以显示和编辑数据的界面控件。它可以作为表格形式展示数据,支持添加、删除、修改、排序和分组操作。
175 0
|
11天前
|
C# 开发者 C++
一套开源、强大且美观的WPF UI控件库
一套开源、强大且美观的WPF UI控件库
127 0