标签归档wpf

WPF TextBox输入显示提示

在网上支付输入银行卡的时候,经常看到输入的数字会放大和提示。

下面是WPF版的一个例子。

 public class ZoomTextTooltip : FrameworkElement
    {
        public object ZoomText
        {
            get { return (object)GetValue(ZoomTextProperty); }
            set { SetValue(ZoomTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ZoomText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ZoomTextProperty =
            DependencyProperty.Register("ZoomText", typeof(object), typeof(ZoomTextTooltip), new UIPropertyMetadata(null,
                (o, args) =>
                {
                    var textBox = args.NewValue as TextBox;
                    if (textBox == null)
                        return;
                    var zoomTextTooltip = o as ZoomTextTooltip;
                    if (zoomTextTooltip == null)
                        return;
                    var popup = new Popup();
                    var textBlock = new TextBlock
                    {
                        Text = textBox.Text,
                        FontWeight = FontWeights.Bold,
                        FontSize = zoomTextTooltip.CustomFontSize,
                        Background = zoomTextTooltip.CustomBackground,
                        Foreground = zoomTextTooltip.CustomForeground
                    };
                    var binding = new Binding();
                    binding.Source = textBox;
                    binding.Path = new PropertyPath("IsKeyboardFocused");
                    BindingOperations.SetBinding(popup, Popup.StaysOpenProperty, binding);

                    var inputText = zoomTextTooltip.AddBlockString(textBox.Text.Trim(), zoomTextTooltip.BlockCount);
                    textBlock.Text = inputText;
                    popup.Child = textBlock;

                    textBox.GotFocus += (sender, eventArgs) =>
                                               {
                                                   popup.PlacementTarget = textBox;
                                                   popup.Placement = PlacementMode.Top;
                                                   popup.IsOpen = true;
                                               };
                    textBox.TextChanged += (sender, eventArgs) =>
                                               {
                                                   var addBlockString = zoomTextTooltip.AddBlockString(textBox.Text.Trim(), zoomTextTooltip.BlockCount);
                                                   textBlock.Text = addBlockString;
                                               };

                    textBox.LostFocus += (sender, eventArgs) =>
                                             {
                                                 popup.IsOpen = false;
                                             };
                }
                ));
        //字符串截取
        private string AddBlockString(string input, int count)
        {
            if (count == 0)
                return input;
            var blockinput = string.Empty;
            var length = Math.Ceiling((double)input.Length / count);
            for (int i = 0; i < length; i++)
            {
                var firstStart = i * count;
                var endString = input.Length - firstStart;
                if (endString < count)
                {
                    blockinput += input.Substring(firstStart);
                }
                else
                {
                    blockinput += input.Substring(firstStart, count);
                    blockinput += " ";
                }

            }
            return blockinput;
        }
        public double CustomFontSize
        {
            get { return (double)GetValue(CustomFontSizeProperty); }
            set { SetValue(CustomFontSizeProperty, value); }
        }

        public static readonly DependencyProperty CustomFontSizeProperty =
            DependencyProperty.Register("CustomFontSize", typeof(double), typeof(ZoomTextTooltip), new UIPropertyMetadata(12.0));

        public Brush CustomBackground
        {
            get { return (Brush)GetValue(CustomBackgroundProperty); }
            set { SetValue(CustomBackgroundProperty, value); }
        }

        public static readonly DependencyProperty CustomBackgroundProperty =
            DependencyProperty.Register("CustomBackground", typeof(Brush), typeof(ZoomTextTooltip), new UIPropertyMetadata(Brushes.White));

        public Brush CustomForeground
        {
            get { return (Brush)GetValue(CustomForegroundProperty); }
            set { SetValue(CustomForegroundProperty, value); }
        }

        public static readonly DependencyProperty CustomForegroundProperty =
            DependencyProperty.Register("CustomForeground", typeof(Brush), typeof(ZoomTextTooltip), new UIPropertyMetadata(Brushes.Black));

        public int BlockCount
        {
            get { return (int)GetValue(BlockCountProperty); }
            set { SetValue(BlockCountProperty, value); }
        }

        public static readonly DependencyProperty BlockCountProperty =
            DependencyProperty.Register("BlockCount", typeof(int), typeof(ZoomTextTooltip), new UIPropertyMetadata(0));



    }
<TextBox  x:Name="tb" /> 
<ControlTest:ZoomTextTooltip CustomFontSize="20"  ZoomText="{Binding ElementName=tb}" BlockCount="4"/>

图示:

image

WPF设计的程序,BLEND做的界面,在XP系统中控件有轻微的错位,部分字体不清楚,部分控件的边缘毛糙,是什么地方做错了?

在win7系统中很正常. 字体设置成display了, 我现在觉得好像不是字体的问题, 因为在xp中整个界面都和win7中有差别, 请帮忙看看问题出在哪里呢?

这是在win7中的截图, 都很正常的

这是xp中的截图, 请看左边的章节位置是一个listBox,显示很正常, 但是其他这些控件就不正常, 加入书签 按钮往左偏了一点,  上面那块黑色很毛糙的地方是一个rectangle , 右边 新宋体 那里是一个comboBox,这个控件边缘还多了一个浅蓝色的边框…. 显示文字的地方是一个richTextBox, 这些文字也是很不清楚, 都像波浪一样的 , 请帮忙看一下吧, 到底是什么地方出了问题

我又测了一下, 是在一部分xp系统上有问题, 有的很正常, 请问有没有可以解决的方法呢, 谢谢了…


Min Zhu

Min Zhu于 

你好,

根据你的描述,非常大的可能是显卡对WPF的兼容性的问题。建议你禁用这些机器上的硬件加速功能,观察一下问题是否仍然存在。如果问题得到修复了,那么就基本可以确认是显卡的问题,建议你先升级对应的显卡驱动程序到最新的版本。如果最新的显卡驱动程序仍然存在这样的问题,那就只能在这些机器上禁用硬件加速功能或者更新硬件了。

WPF Step By Step 完整布局介绍

回顾

        上一篇,我们介绍了基本控件及控件的重要属性和用法,我们本篇详细介绍WPF中的几种布局容器及每种布局容器的使用场景,当

然这些都是本人在实际项目中的使用经验,可能还存在错误之处,还请大家指出。

本文大纲

1、Grid

2、StackPanel

3、DockPanel

4、WrapPanel

Grid

1、Row和Column

我们下面来介绍Grid的行的用法,及我们在UI设计过程中需要注意的细节。

由于前面我们在第一章中已经介绍了基本的关于Grid的表格行和列的定义及相关属性,为了防止大家遗忘,我们这里再次介绍下:

image

为了加深大家对Grid布局的印象,我们这里加入控件来展示效果。

下面在每个单元格都加入子控件

image

上面指定了控件在Grid表格中的哪一行那一列,如果我们的某个控件跨行或者跨列如何做呢?

image

关于跨行和跨列一样,只不过将Grid.ColumnSpan换成Grid.RowSpan。

下面介绍,在Grid如何将控件设置为自适应宽度和高度,或者是固定宽度或固定高度时,应该注意的细节。

image

1、自适应区域:

image

2、顶部对齐或底部对齐

image

对于顶部对齐和底部对齐,相对来说都一样。

3、左右对齐时:

image

4、下面来举个例子,我们来如何分析,根据原型来使用Grid布局来达到要求和目标:

例如下图:

image

我们以博客园为例,可能例子不太合适,但是如果我们想做一个博客园的桌面版,保持风格一致的情况下,如果我们使用Grid布局如何来布局呢?

A、有Logo图片,上面还有设置等菜单,所以,我们可以吧这块设置为二行,这样比较容易区分页面的布局和设置

B、下面有几个二级菜单,新闻、博问等 一行

C、左侧有网站分类。必须1列

D、右侧有内容区。上面有区分首页、精华、候选、新闻、关注等、1列

E、右侧有找找看、还有最新新闻等 1列。

F、最下面,肯定还有状态栏,如果我们开发桌面系统。1行

 

根据上面的分析,我们的Grid表格至少5行、3列

关于其他的设计,我们通过Grid表格的组合来进行控制。

下面我们就来实现下:

先设置大体布局如下:

image

关于上述布局的具体实现如下:

<Window x:Class=”Samples.MainWindow”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”MainWindow” Height=”600″ Width=”800″>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height=”20″/>
<RowDefinition Height=”50″/>
<RowDefinition Height=”30″/>
<RowDefinition Height=”*”/>
<RowDefinition Height=”30″/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=”150″/>
<ColumnDefinition Width=”*”/>
<ColumnDefinition Width=”200″/>
</Grid.ColumnDefinitions>
<Grid Grid.Column=”1″ Grid.ColumnSpan=”2″>
<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right”>
<Button Content=”何戈洲” Margin=”5,0,0,0″/>
<Button Content=”我的博客” Margin=”5,0,0,0″/>
<Button Content=”短消息” Margin=”5,0,0,0″/>
<Button Content=”设置” Margin=”5,0,0,0″/>
<Button Content=”退出” Margin=”5,0,0,0″/>
</StackPanel>
</Grid>
<Grid Grid.Column=”0″ Grid.Row=”1″>
<Image Source=”/Samples;Component/Images/logo_small.gif” />
</Grid>
<Grid Grid.Column=”0″ Grid.ColumnSpan=”3″ Grid.Row=”2″>
<StackPanel Orientation=”Horizontal”>
<Button Margin=”5,0,0,0″>园子</Button>
<Button Margin=”5,0,0,0″>新闻</Button>
<Button Margin=”5,0,0,0″>博问</Button>
<Button Margin=”5,0,0,0″>闪存</Button>
<Button Margin=”5,0,0,0″>网摘</Button>
<Button Margin=”5,0,0,0″>招聘</Button>
<Button Margin=”5,0,0,0″>专题</Button>
<Button Margin=”5,0,0,0″>知识</Button>
</StackPanel>
</Grid>
<Grid Grid.Column=”0″ Grid.ColumnSpan=”3″ Grid.Row=”3″>
<Image Source=”/Samples;Component/Images/main.png” />
</Grid>
<Grid Grid.Column=”0″ Grid.ColumnSpan=”3″ Grid.Row=”4″>
<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Center”>
<Button Margin=”5,0,0,0″>关于我们</Button>
<Button Margin=”5,0,0,0″>联系我们</Button>
<Button Margin=”5,0,0,0″>广告服务</Button>
<Button Margin=”5,0,0,0″>人才服务</Button>
<Button Margin=”5,0,0,0″>版权</Button>
</StackPanel>
</Grid>
</Grid>
</Window>

 

从上面的代码可以看出来,非常的简单,Grid特别适合软件系统的整体布局,在实际的项目中通过Grid与其他的布局控件相结合一起完成页面的整体布局。

StackPanel

StackPanel 适合水平或者垂直方向的布局,在上面的例子中我们大量的使用该种布局方式。适合局部区域的布局。比如博客园中的如下区域就可以采用StackPanel进行布局。

image

image

image

对于这类的固定的区域,我们可以不适用Grid来进行布局,使用StackPanel也可以达到目标。

我们来使用StackPanel来进行布局

<StackPanel Orientation=”Vertical” VerticalAlignment=”Stretch”>
<GroupBox Header=”网站分类” Height=”Auto”>
<StackPanel Orientation=”Vertical”>
<Button Content=”.NET技术(16)”/>
<Button Content=”编程语言(13)”/>
<Button Content=”软件设计(3)”/>
<Button Content=”Web前端(16)”/>
<Button Content=”软件工程(26)”/>
</StackPanel>
</GroupBox>
<GroupBox Header=”链接” Height=”Auto”>
<StackPanel Orientation=”Vertical”>
<Button Content=”反馈和建议”/>
<Button Content=”官方博客”/>
<Button Content=”电子期刊” />
<Button Content=”人才服务”/>
<Button Content=”博客模板”/>
</StackPanel>
</GroupBox>
</StackPanel>

运行效果如下:

image

与预期的效果相同,对于其他的模块,我们也可以在局部,对于水平或者垂直方向要求进行布局的,我们都可以采用StackPanel来进行布局。

下面我们来看看横向布局的例子:

image

我们通过表格中的使用对StackPanel的停靠定位,进而通过Stackpanel对内部的子控件的停靠方向设置,我们通过如下代码实现上述效果:

<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right”>
<Button Content=”何戈洲” Margin=”5,0,0,0″/>
<Button Content=”我的博客” Margin=”5,0,0,0″/>
<Button Content=”短消息” Margin=”5,0,0,0″/>
<Button Content=”设置” Margin=”5,0,0,0″/>
<Button Content=”退出” Margin=”5,0,0,0″/>
</StackPanel>

StackPanel在父容器中是右对齐的。

然后再StackPanel容器中,如果也采用内容右对齐,会有什么效果呢?

<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right”>
<Button Content=”何戈洲” Margin=”5,0,0,0″ HorizontalAlignment=”Right”/>
<Button Content=”我的博客” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”短消息” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”设置” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”退出” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
</StackPanel>

设置子控件的停靠方式时,不会起到任何作用,默认情况下,Stack的水平布局时,从左至右。

<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right” FlowDirection=”RightToLeft”>
<Button Content=”何戈洲” Margin=”5,0,0,0″ HorizontalAlignment=”Right”/>
<Button Content=”我的博客” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”短消息” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”设置” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”退出” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
</StackPanel>

修改了FlowDirection设置了StackPanel的方向后,所有的子控件,都是从右向左方向进行绘制和显示,效果如下:

image

所以对于StackPanel我们基本上是用上述的属性和对StackPanel的停靠方式进行设置后,即可满足布局的要求。

DockPanel

DockPanel停靠容器,专门负责自适应窗口的布局,之前我们介绍了DockPanel的布局设置,这里再回顾下:

<DockPanel>
<StackPanel DockPanel.Dock=”Top” Height=”0″>
</StackPanel>
<StackPanel DockPanel.Dock=”Left” Height=”0″>
</StackPanel>
<StackPanel DockPanel.Dock=”Bottom” Height=”0″>
</StackPanel>
<StackPanel DockPanel.Dock=”Right” Orientation=”Vertical” Width=”200″>
<GroupBox Header=”最新新闻” Height=”160″>
<StackPanel Orientation=”Vertical”>
<Button Content=”宅急送近日宣布降价抢”/>
<Button Content=”腾讯联手华为操盘四核手机”/>
<Button Content=”Windows 8各版本区别与售价”/>
<Button Content=”数方程将无线网络带宽提高一个数量级”/>
<Button Content=”中移动:Lumia 920T将于11月上市”/>
<Button Content=”Windows 8下一站:10月25日纽约”/>
</StackPanel>
</GroupBox>
<GroupBox Header=”48小时阅读排行榜” Height=”160″>
<StackPanel Orientation=”Vertical”>
<Button Content=”子用户-角色-权限-菜单 浅谈:子账户设计方案”/>
<Button Content=”网站已恢复正常,让大家久等了”/>
<Button Content=”拿什么拯救你,我的51Job简历?——UBB漏洞”/>
<Button Content=”这些年我们没用过的JS”/>
<Button Content=”多少钱才可让人重拾理想”/>
<Button Content=”准备购买的Dell服务器的硬件配置”/>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel >

<Button Content=” 我铺满”/>

</StackPanel>
</DockPanel>

上面的DockPanel在进行自适应布局时,默认最后的一个区域时默认填充,可以理解为fill。而必须制定其他的区域后,该设置才有效,所以,我们上面设置了top,left,bottom 占用的空间都是0,这样,系统会将最后的一个子区域填充。

上面设置后的效果如下。

image

当然,这个页面的整体,我们也可以采用DockPanel进行布局,布局的效果,完全可以达到上述效果,下面我们来使用DockPanel来对整体进行布局吧。

最终的代码如下:

<DockPanel>
<StackPanel DockPanel.Dock=”Top” Height=”100″>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=”20″/>
<RowDefinition Height=”50″/>
<RowDefinition Height=”30″/>
</Grid.RowDefinitions>
<Grid >
<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right” FlowDirection=”RightToLeft”>
<Button Content=”何戈洲” Margin=”5,0,0,0″ HorizontalAlignment=”Right”/>
<Button Content=”我的博客” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”短消息” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”设置” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”退出” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
</StackPanel>
</Grid>
<Grid  Grid.Row=”1″>
<Image Source=”/Samples;Component/Images/logo_small.gif” HorizontalAlignment=”Left”/>
</Grid>
<Grid  Grid.Row=”2″>
<StackPanel Orientation=”Horizontal”>
<Button Margin=”5,0,0,0″>园子</Button>
<Button Margin=”5,0,0,0″>新闻</Button>
<Button Margin=”5,0,0,0″>博问</Button>
<Button Margin=”5,0,0,0″>闪存</Button>
<Button Margin=”5,0,0,0″>网摘</Button>
<Button Margin=”5,0,0,0″>招聘</Button>
<Button Margin=”5,0,0,0″>专题</Button>
<Button Margin=”5,0,0,0″>知识</Button>
</StackPanel>
</Grid>
</Grid>
</StackPanel>
<StackPanel DockPanel.Dock=”Bottom” Height=”30″ Orientation=”Horizontal” HorizontalAlignment=”Center”>
<Button Margin=”5,0,0,0″>关于我们</Button>
<Button Margin=”5,0,0,0″>联系我们</Button>
<Button Margin=”5,0,0,0″>广告服务</Button>
<Button Margin=”5,0,0,0″>人才服务</Button>
<Button Margin=”5,0,0,0″>版权</Button>
</StackPanel>
<StackPanel DockPanel.Dock=”Left” Width=”150″>
<StackPanel Orientation=”Vertical” VerticalAlignment=”Stretch”>
<GroupBox Header=”网站分类” Height=”Auto”>
<StackPanel Orientation=”Vertical”>
<Button Content=”.NET技术(16)”/>
<Button Content=”编程语言(13)”/>
<Button Content=”软件设计(3)”/>
<Button Content=”Web前端(16)”/>
<Button Content=”软件工程(26)”/>
</StackPanel>
</GroupBox>
<GroupBox Header=”链接” Height=”Auto”>
<StackPanel Orientation=”Vertical”>
<Button Content=”反馈和建议”/>
<Button Content=”官方博客”/>
<Button Content=”电子期刊” />
<Button Content=”人才服务”/>
<Button Content=”博客模板”/>
</StackPanel>
</GroupBox>
</StackPanel>
</StackPanel>
<StackPanel DockPanel.Dock=”Right” Orientation=”Vertical” Width=”200″>
<GroupBox Header=”最新新闻” Height=”160″>
<StackPanel Orientation=”Vertical”>
<Button Content=”宅急送近日宣布降价抢”/>
<Button Content=”腾讯联手华为操盘四核手机”/>
<Button Content=”Windows 8各版本区别与售价”/>
<Button Content=”数方程将无线网络带宽提高一个数量级”/>
<Button Content=”中移动:Lumia 920T将于11月上市”/>
<Button Content=”Windows 8下一站:10月25日纽约”/>
</StackPanel>
</GroupBox>
<GroupBox Header=”48小时阅读排行榜” Height=”160″>
<StackPanel Orientation=”Vertical”>
<Button Content=”子用户-角色-权限-菜单 浅谈:子账户设计方案”/>
<Button Content=”网站已恢复正常,让大家久等了”/>
<Button Content=”拿什么拯救你,我的51Job简历?——UBB漏洞”/>
<Button Content=”这些年我们没用过的JS”/>
<Button Content=”多少钱才可让人重拾理想”/>
<Button Content=”准备购买的Dell服务器的硬件配置”/>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel >
<Button Content=” 我铺满”/>
</StackPanel>
</DockPanel>

 

运行上述代码效果如下:

image

通过DockPanel完成对整体的布局,然后内部结合一些其他的布局控件,完成局部的细节的布局。

 

 

 

WrapPanel

WrapPanel容器我们也介绍过,该容器可以看做自动换行功能的StackPanel容器。下面我们就来分析下该容器的一般应用场景。

image 我们看到了windows8中的如下页面,如果我们仿制该页面的时候,其实我们可以采用wrappanel来实现自动的换行,下面我们来试试吧

最终代码如下:

<Window x:Class=”Samples.Window8Window”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”Window8Window” Height=”600″ Width=”800″>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=”50″/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<Grid Grid.Row=”0″>
<Label Content=”开始” FontFamily=”微软雅黑” FontSize=”30″/>
</Grid >
<Grid Grid.Row=”1″>
<WrapPanel Orientation=”Horizontal” ItemHeight=”100″ ItemWidth=”190″>
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
</WrapPanel>
</Grid>
</Grid>
</Window>

 

运行后,效果如下:

image

当然,我们的界面效果,还打不到美感,但是的确是自动换行。我们将水平方向,修改为垂直方向后,运行:

image

运行查看效果。

image

通过上面的简单案例,我们基本上知道了wrapPanel的用法。

 

总结

通过上面的介绍和demo的演示,我们知道了如何在项目中什么情况下,使用什么样的布局容器,通过实际的案例,我们更容易理解和掌握布局的模式。错误之处,还请大家反馈,我及时改正,谢谢!

WPF注销系统与退出系统实现

WPF在系统设计的时候少不了登陆,登出,退出系统!

那么注销系统和退出系统究竟该怎样做呢!

首先行不通的事情就是直接关闭登陆页面,也就是起始页面,这样的话系统真的就退出,因此采用的方法应该是在登陆成功之后,隐藏掉登陆页面。

那么我们又怎样来进行登出操作呢?退出系统操作呢?

WPF注销系统与退出系统实现

 

在关闭的时候,我们可能在MainWindow中直接有退出系统按钮,那么我们在点击的时候直接调用Application.Current.ShutDownOK,关闭了,没有问题,但是如果我们不是点击的退出系统按钮,而是直接点击Window自带的关闭呢,结果是什么?可想而知,我们只是退出了当前的主页,而没有退出系统,因为还有登陆页面在隐藏。

因此我们用上面的关闭方法是行不通的,那么到底该怎样做呢,其实很简单,我们知道每个窗体都有Closing的事件,只要我们捕捉到加以利用,一切就OK了,因此我们在MianWindow窗体初始化的时候就监听它的Closing事件,这样无论我们点击了什么,只要当前的窗体关闭了,就相当于关闭了整个系统。

问题还是存在,那么如果我们又想注销系统怎么办呢?当然我们得关闭MainWindow,但是我们不是真想关闭系统而是想显示Login窗体,这样我们以上的做法不是不合理了?难道要改套路不成?

不是的,我们可以设置在MianWindow中设置一个bool的全局变量,默认值为trueClosing事件中,我们只需要判断一下这个变量就可以了,如果为true,那么就关闭系统,如果为false,则显示Login窗体,(当然,在那个页面进行控制,则在哪个页面就应该传递过来Login窗体的对象,我认为比较好用的方法是定义一个属性,通过传递来进行,例如在Login打开MianWidow的时候,我们就将MainWindow.Login=this),清空该用户的信息,OK,那么我们什么时候设置那个全局变量呢?当然是在我们点击注销按钮的时候了,在点击注销按钮的时候,就两个动作,一个是将全局的变量设置成False,另一重要,就是关闭自身,this.close()OK 这样就完成了。

C# WPF 中用代码模拟鼠标和键盘的操作

原文地址
C#开发者都知道,在Winform开发中,SendKeys类提供的方法是很实用的。但是可惜的是,在WPF中不能使用这个方法了。
我们知道,在WPF中非UI线程刷新UI线程,需要使用Dispatcher.Invoke((Action)delegate { /* Your
code is put here */ });
方法。这里调用System.Windows.Forms.SendKeys.Send()方法会报错。

下面这个代码文件做了一个很好的包装,可以下载后参考:
Simulation.zip

如何使用呢?
很简单, 要敲一个键, 比如回车:
Keyboard.Press(Key.Enter);
Keyboard.Release(Key.Enter);

要敲一个组合键:比如Alt+F4:
Keyboard.Press(Key.LeftAlt);
Keyboard.Press(Key.F4);
Keyboard.Release(Key.LeftAlt);
Keyboard.Release(Key.F4);

要敲一段文字:
Keyboard.Type(“notepad”);

鼠标与之类似,比如:
Mouse.MoveTo(new System.Drawing.Point(x, y));
Mouse.Click(MouseButton.Right);

Simulation类的定义如下:

//定义是这样的:

[DllImport("user32.dll", SetLastError = true)]
internal static extern int SendInput(int nInputs, ref INPUT mi, int cbSize);

//其中的INPUT结构表示一个键盘或鼠标操作:

[StructLayout(LayoutKind.Sequential)]
internal struct INPUT
{
internal int type;
internal INPUTUNION union;
};
[StructLayout(LayoutKind.Explicit)]
internal struct INPUTUNION
{
[FieldOffset(0)]
internal MOUSEINPUT mouseInput;
[FieldOffset(0)]
internal KEYBDINPUT keyboardInput;
};
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEINPUT
{
internal int dx;
internal int dy;
internal int mouseData;
internal int dwFlags;
internal int time;
internal IntPtr dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDINPUT
{
internal short wVk;
internal short wScan;
internal int dwFlags;
internal int time;
internal IntPtr dwExtraInfo;
};
[Flags]
internal enum SendMouseInputFlags
{
Move = 0x0001,
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
XDown = 0x0080,
XUp = 0x0100,
Wheel = 0x0800,
Absolute = 0x8000,
};

WPF DocumentViewer隐藏工具栏和搜索栏

<Style x:Key=”{x:Type DocumentViewer}” TargetType=”{x:Type DocumentViewer}”>

<Setter Property=”Foreground” Value=”{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}” />

<Setter Property=”Background” Value=”{DynamicResource {x:Static SystemColors.ControlBrushKey}}” />

<Setter Property=”FocusVisualStyle” Value=”{x:Null}” />

<Setter Property=”Template”>

<Setter.Value>

<ControlTemplate TargetType=”{x:Type DocumentViewer}”>

<Border BorderThickness=”{TemplateBinding BorderThickness}” BorderBrush=”{TemplateBinding BorderBrush}” Focusable=”False”>

<Grid KeyboardNavigation.TabNavigation=”Local”>

<Grid.Background>

<SolidColorBrush Color=”{DynamicResource ControlLightColor}” />

</Grid.Background>

<Grid.RowDefinitions>

<RowDefinition Height=”Auto” />

<RowDefinition Height=”*” />

<RowDefinition Height=”Auto” />

</Grid.RowDefinitions>

<ScrollViewer Grid.Row=”1″ CanContentScroll=”true” HorizontalScrollBarVisibility=”Auto” x:Name=”PART_ContentHost” IsTabStop=”true”>

<ScrollViewer.Background>

<LinearGradientBrush EndPoint=”0.5,1″ StartPoint=”0.5,0″>

<GradientStop Color=”{DynamicResource ControlLightColor}” Offset=”0″ />

<GradientStop Color=”{DynamicResource ControlMediumColor}” Offset=”1″ />

</LinearGradientBrush>

</ScrollViewer.Background>

</ScrollViewer>

</Grid>

</Border>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

关于 WPF PrintDialog 设置打印纸张高度与宽度问题。

    • 问题如下:

      我通过WPF进行打印,WPF中PrintDialog默认纸张大小应该是A4,宽度是700多左右,高度是1000多。当我想要在X轴1000的位置打印文字时,总是打印不出来。因此我想通过设置打印纸张的大小来让X轴1000处的文字打印出来。代码如下。设置PageMediaSize为 ISOA0,应该是3000*4000多。但是我还是无法将X轴1000处的文字打印出来。网上搜了很久无果。因此在这里提问。希望能解决这个问题。代码如下。

      private void button_SinglePointPrint_Click(object sender, RoutedEventArgs e)
      {
      PrintDialog pDialog = new PrintDialog();
      pDialog.PrintTicket = pt;
      pDialog.PrintQueue = pq;
      pDialog.PrintTicket.PageMediaSize = new PageMediaSize(PageMediaSizeName.ISOA0);
      if (pDialog.ShowDialog() == true)
      {
      pt = pDialog.PrintTicket;
      pq = pDialog.PrintQueue;

      DrawingVisual vis = new DrawingVisual();
      DrawingContext dc = vis.RenderOpen();

      dc.DrawText(pLib.GetFormattedText(textBox_SinglePointText.Text, TextAlignment.Center, new Typeface(fontName), fontSize), new Point(1000,100));
      dc.Close();

      pDialog.PrintVisual(vis, “TestSinglePoint”);
      }
      }

      2014年4月21日 15:38
      kouxuelong 的头像

      0 分数

答案

  • 你好,

    你可以通过设置PrintTicket.PageMediaSizeSize,然后Transform你的窗体大小。请查看下面代码:

     private void _print()
     {
          PrintDialog printDlg = new System.Windows.Controls.PrintDialog();
    
           PrintTicket pt = printDlg.PrintTicket;
           Double printableWidth = pt.PageMediaSize.Width.Value;
           Double printableHeight = pt.PageMediaSize.Height.Value;
    
           Double xScale = (printableWidth - xMargin * 2) / printableWidth;
           Double yScale = (printableHeight - yMargin * 2) / printableHeight;
    
           this.Transform = new MatrixTransform(xScale, 0, 0, yScale, xMargin, yMargin);
    
        //now print the visual to printer to fit on the one page.
         printDlg.PrintVisual(this, "Print Page");
     }

    谢谢!

WPF小知识,MessageBox的多种用法

我们在程序中经常会用到MessageBox。

现将其常见用法总结如下:

1.MessageBox.Show(“Hello~~~~”);

最简单的,只显示提示信息。

2.MessageBox.Show(“There are something wrong!”,”ERROR”);

可以给消息框加上标题。

3.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel) == DialogResult.OK)

{

//delete

}

询问是否删除时会用到这个。

4.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel,MessageBoxIcon.Question) == DialogResult.OK)

{

//delete

}

可以给MessageBox加上一个Icon,.net提供常见的Icon共选择。

5.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question,MessageBoxDefaultButton.Button2) == DialogResult.OK)

{

//delete

}

可以改变MessageBox的默认焦点,如下:

6.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question,MessageBoxDefaultButton.Button2,MessageBoxOptions.RtlReading) == DialogResult.OK)

{

//delete

}

反向显示:

7.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2, MessageBoxOptions.RightAlign,true) == DialogResult.OK)

{

//delete

}

添加Help按钮:

8.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1, MessageBoxOptions.RtlReading, @”/folder/file.htm”) == DialogResult.OK)

{

//delete

}

指定帮助文件的路径,点击即可打开该路径下的帮助文件。

9.//HelpNavigator指定常数来指示要显示的帮助文件元素。Find 帮助文件将打开到搜索页。

if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1, MessageBoxOptions.RtlReading, @”/folder/file.htm”, HelpNavigator.Find) == DialogResult.OK)

{

//delete

}

还有一些用法,不是太实用这里就不一一介绍了,有兴趣的朋友可以参考下这里:MSDN的MessageBox类。

========================================================================
【函数】 <整型> MessageBox(<字符串> Text, <字符串> Title, <整型> nType,MessageBoxIcon);
【函数说明】 弹出一个消息框。
【语法】
参数:
Text <字符串>,消息框的正文;
Title <字符串>,消息框的标题;
nType <整型>,消息框的类型。
返回值:<整型>,用户在消息框上点击关闭时的选择的按钮。 MessageBoxIcon:对话框上显示的图标样式。

【说明】
MessageBox(“消息内容”, “返回值 确定1”,MessageBoxButtons.OK,MessageBoxIcon.Question);
MessageBox(“消息内容”,, “返回值 确定1 取消2”,MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);
MessageBox(“消息内容”, “返回值 终止3 重试4 忽略5”,MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error);
MessageBox(“消息内容”, “返回值 是6 否7 取消2”,MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation);
MessageBox(“消息内容”, “返回值 是6 否7”,MessageBoxButtons.YesNo, MessageBoxIcon.Hand);
MessageBox(“消息内容”, “返回值 重试4 取消2”,MessageBoxButtons.RetryCancel, MessageBoxIcon.Information);

MessageBoxIcon: 所有图标样式

MessageBoxIcon.Question MessageBoxIcon.Asterisk MessageBoxIcon.Information MessageBoxIcon.Error MessageBoxIcon.Stop MessageBoxIcon.Hand MessageBoxIcon.Exclamation MessageBoxIcon.Warning MessageBoxIcon.None

MessageBox函数MessageBox()函数MessageBox是标准的windows Api函数只能在CWnd类的继承类中使用,在C#中使用时,通常用MessageBox的show方法来实现对话框的弹出,命名空间System.Windows.Forms

应用实例:

DialogResult 是枚举类可以用枚举值直接比较MessageBox的返回值也可以转换为整型后再比较。如下:DialogResult r1 = MessageBox.Show ( “是否确定?” , “垃圾处理!” , MessageBoxButtons.AbortRetryIgnore , MessageBoxIcon.Question ) ;
int ss1=(int)r1 ;
if ( ss1==3 ){ }
if ( ss1==4 ){ }
if ( ss1==5){ }

或者是

if (DialogResult.Yes == MessageBox.Show(“232”, “”, MessageBoxButtons.YesNo, MessageBoxIcon.Information,MessageBoxDefaultButton.Button1))
{
MessageBox.Show(“122”);
}

WPF有一个 button, button 的背景是一张图片, 当我运行程序后, 点击button的时候, button会有按下的效果,但同时 背景图片也消失了, 等松开鼠标的时候,背景图片又回来了,

回复次数:4

WPF后台动态调用样式文件

应用场合:如果您的WPF应用程序设置WPF运行一个实例代码后,App.xaml文件中对样式资源字典文件的引用将失效.

解决办法1:在App.xaml.cs文件中用反射动态调用另外一个DLL项目中的样式文件即可

详细操作介绍如下:

1、WPF设置只运行一个实例代码:

App.xaml文件代码如下:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source=”ButtonStyle.xaml”/>
</ResourceDictionary>
</Application.Resources>
</Application>

App.xaml.cs文件代码如下:

//添加引用
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace WpfUI
{

/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{

public App()
{
}

/// <summary>
/// 要设置App.xaml的文件属性中生成操作=无
/// </summary>
[STAThread]
public static void Main()
{
App myApp = new App();
myApp.ShutdownMode = ShutdownMode.OnExplicitShutdown;
myApp.Run();
}

private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (e.Exception is InvalidOperationException)
e.Handled = true;
}

protected override void OnStartup(StartupEventArgs e)
{
//获取当前运行WPF程序的进程实例
Process process = Process.GetCurrentProcess();
//遍历WPF程序的同名进程组
foreach (Process p in Process.GetProcessesByName(process.ProcessName))
{
if (p.Id != process.Id && (p.StartTime – process.StartTime).TotalMilliseconds <= 0)
{
p.Kill();//关闭进程
return;
}
}
base.OnStartup(e);
//启动登陆窗体,

MainWindow myWindow = new MainWindow();

myWindow.Show();
}

}

}

2、ButtonStyle.xaml样式文件内容如下:

<ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml“>
<!–按钮样式–>
<Style x:Key=”RedButtonStyle” TargetType=”{x:Type Button}”>
<Setter Property=”Foreground” Value=”Red”/>
<Setter Property=”Background” Value=”Silver”/>
<Setter Property=”Height” Value=”23″/>
</Style>

</ResourceDictionary>

3、MainWindow.xaml文件内容如下:

<Window x:Class=”WpfUI.MainWindow”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml
Title=”MainWindow” Height=”350″ Width=”525″>
<Grid>
<Button Content=”Button” Style=”{StaticResource RedButtonStyle}” Height=”23″ HorizontalAlignment=”Left” Margin=”102,66,0,0″ Name=”button1″ VerticalAlignment=”Top” Width=”75″ />
</Grid>
</Window>
4、运行程序后发现按钮样式RedButtonStyle总提示找不到

5、 解决办法如下:

第一步: 新建一个Windows–类库项目WpfThems,将ButtonStyle.xaml拷贝过去, 设置ButtonStyle.xaml文件的属性生成操作为 “Page”,之后生成WpfThems.dll文件

第二步:在当前项目WpfUI中添加WpfThems项目引用

第三步:修改App.xaml.cs 文件代码:添加一个函数LoadStyleResource并修改OnStartup函数内容。修改后的App.xaml.cs文件内容如下:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
//添加引用
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace WpfUI
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{

public App()
{
}

/// <summary>
/// 设置WpfUI项目中的App.xaml的文件属性中”生成操作”为”无”
/// 设置WpfThemes项目中的ButtonStyle.xaml的文件属性中”生成操作”为”Page”
/// </summary>
[STAThread]
public static void Main()
{
App myApp = new App();
myApp.ShutdownMode = ShutdownMode.OnExplicitShutdown;
myApp.Run();
}

/// <summary>
/// 重载应用程序启动函数
/// </summary>
/// <param name=”e”></param>
protected override void OnStartup(StartupEventArgs e)
{
//获取当前运行WPF程序的进程实例
Process process = Process.GetCurrentProcess();
//遍历WPF程序的同名进程组
foreach (Process p in Process.GetProcessesByName(process.ProcessName))
{
if (p.Id != process.Id && (p.StartTime – process.StartTime).TotalMilliseconds <= 0)
{
p.Kill();//关闭进程
return;
}
}
base.OnStartup(e);
//动态调用样式文件
LoadStyleResource();

//启动窗体
MainWindow myWindow = new MainWindow();
myWindow.Show();
}
private void LoadStyleResource()
{
Assembly assembly = Assembly.LoadFrom(“WpfThemes.dll”);
string packUri = @”/WpfThemes;component/ButtonStyle.xaml”;
ResourceDictionary myResourceDictionary = Application.LoadComponent(new Uri(packUri, UriKind.Relative)) as ResourceDictionary;
this.Resources.MergedDictionaries.Add(myResourceDictionary);
}
}
}

 

解决办法2:在每个窗体的xaml文件中添加对指定样式文件的引用

<Window.Resources>

<ResourceDictionary>

<ResourceDictionary.MergedDictionaries>

<ResourceDictionary Source=”ButtonStyle.xaml”/>

</ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

</Window.Resources>

详细工程项目请到我的下载资源中下载: http://download.csdn.net/detail/xqf222/5582575

WPF 登录窗口关闭时打开主窗口

在WPF中设计登录窗口关闭时打开主窗口,自动生成的App.xaml不能满足要求,

1、把App.xaml的属性窗口中的生成操作设定为 无

2、添加Program类

复制代码
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            LoginWindow loginForm = new LoginWindow();
            loginForm.Init();
            bool? rt = loginForm.ShowDialog();
            loginForm.Close();
            if (rt == true)
            {
                Application App = new Application();
                App.ShutdownMode = ShutdownMode.OnMainWindowClose;
                MainWindow m_MianWindow = new MainWindow();
                App.MainWindow = m_MianWindow;
                App.Run(m_MianWindow);
            }
        }
    }
复制代码

这样就可以满足要求了

参考:http://www.mysjtu.com/page/M0/S613/613036.html

 

方法二:

最近看一方法,不用添加Program方法即可,代码如下:

复制代码
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            Application.Current.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;
            LoginWindow window = new LoginWindow();
            bool? dialogResult = window.ShowDialog();
            if ((dialogResult.HasValue == true) &&
                (dialogResult.Value == true))
            {
                base.OnStartup(e);
                Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
            }
            else
            {
                this.Shutdown();
            }
        }
    }
复制代码

通过Application的ShutdownMode控制进程的运行时间。

WPF 后台任务 等待动画 样例

运行效果:

前台代码:

[csharp] view plain copy

  1. <Window x :Class=“Waiting.Window1”
  2.         xmlns=“http://schemas.microsoft.com/winfx/2006/xaml/presentation”
  3.         xmlns:x =“http://schemas.microsoft.com/winfx/2006/xaml”
  4.         Title=“后台忙” Height=“94.635” Width=“197.361”>
  5.     <Grid >
  6.         <Button Content =“开始” HorizontalAlignment=“Left” Margin=“10,10,0,0” Name =“button1” VerticalAlignment=“Top” Click=“button1_Click” Width =“48” />
  7.         <Label Name =“lab_pro” Content=“” Height=“25” VerticalAlignment =“Top” HorizontalAlignment=“Left” Margin=“80,20,0,0” />
  8.         <!–动画代码,只要填写name属性即可–>
  9.         <Grid Name =“loading” Visibility=“Collapsed” Height=“41” Grid.Row =“0” VerticalAlignment=“Top” Margin=“126,10,0,0” HorizontalAlignment=“Left” Width =“42”>
  10.             <Grid.Resources>
  11.                 <DrawingBrush x :Key=“brush” Stretch=“None” AlignmentX =“Center” AlignmentY=“Top”>
  12.                     <DrawingBrush.Drawing>
  13.                         <GeometryDrawing Brush =“Black”>
  14.                             <GeometryDrawing.Geometry>
  15.                                 <EllipseGeometry RadiusX =“2” RadiusY=“5”/>
  16.                             </GeometryDrawing.Geometry>
  17.                         </GeometryDrawing>
  18.                     </DrawingBrush.Drawing>
  19.                 </DrawingBrush>
  20.             </Grid.Resources>
  21.             <Rectangle x :Name=“r01” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  22.                 <Rectangle.RenderTransform>
  23.                     <RotateTransform Angle =“0”/>
  24.                 </Rectangle.RenderTransform>
  25.             </Rectangle>
  26.             <Rectangle x :Name=“r02” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  27.                 <Rectangle.RenderTransform>
  28.                     <RotateTransform Angle =“30”/>
  29.                 </Rectangle.RenderTransform>
  30.             </Rectangle>
  31.             <Rectangle x :Name=“r03” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  32.                 <Rectangle.RenderTransform>
  33.                     <RotateTransform Angle =“60”/>
  34.                 </Rectangle.RenderTransform>
  35.             </Rectangle>
  36.             <Rectangle x :Name=“r04” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  37.                 <Rectangle.RenderTransform>
  38.                     <RotateTransform Angle =“90”/>
  39.                 </Rectangle.RenderTransform>
  40.             </Rectangle>
  41.             <Rectangle x :Name=“r05” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  42.                 <Rectangle.RenderTransform>
  43.                     <RotateTransform Angle =“120”/>
  44.                 </Rectangle.RenderTransform>
  45.             </Rectangle>
  46.             <Rectangle x :Name=“r06” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  47.                 <Rectangle.RenderTransform>
  48.                     <RotateTransform Angle =“150”/>
  49.                 </Rectangle.RenderTransform>
  50.             </Rectangle>
  51.             <Rectangle x :Name=“r07” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  52.                 <Rectangle.RenderTransform>
  53.                     <RotateTransform Angle =“180”/>
  54.                 </Rectangle.RenderTransform>
  55.             </Rectangle>
  56.             <Rectangle x :Name=“r08” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  57.                 <Rectangle.RenderTransform>
  58.                     <RotateTransform Angle =“210”/>
  59.                 </Rectangle.RenderTransform>
  60.             </Rectangle>
  61.             <Rectangle x :Name=“r09” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  62.                 <Rectangle.RenderTransform>
  63.                     <RotateTransform Angle =“240”/>
  64.                 </Rectangle.RenderTransform>
  65.             </Rectangle>
  66.             <Rectangle x :Name=“r10” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  67.                 <Rectangle.RenderTransform>
  68.                     <RotateTransform Angle =“270”/>
  69.                 </Rectangle.RenderTransform>
  70.             </Rectangle>
  71.             <Rectangle x :Name=“r11” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  72.                 <Rectangle.RenderTransform>
  73.                     <RotateTransform Angle =“300”/>
  74.                 </Rectangle.RenderTransform>
  75.             </Rectangle>
  76.             <Rectangle x :Name=“r12” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  77.                 <Rectangle.RenderTransform>
  78.                     <RotateTransform Angle =“330”/>
  79.                 </Rectangle.RenderTransform>
  80.             </Rectangle>
  81.             <Grid.Triggers>
  82.                 <EventTrigger RoutedEvent =“Grid.Loaded”>
  83.                     <BeginStoryboard>
  84.                         <Storyboard RepeatBehavior =“Forever”>
  85.                             <DoubleAnimation Storyboard.TargetName =“r01” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.00000” To=“0”/>
  86.                             <DoubleAnimation Storyboard.TargetName =“r02” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.08333” To=“0”/>
  87.                             <DoubleAnimation Storyboard.TargetName =“r03” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.16666” To=“0”/>
  88.                             <DoubleAnimation Storyboard.TargetName =“r04” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.24999” To=“0”/>
  89.                             <DoubleAnimation Storyboard.TargetName =“r05” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.33332” To=“0”/>
  90.                             <DoubleAnimation Storyboard.TargetName =“r06” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.41665” To=“0”/>
  91.                             <DoubleAnimation Storyboard.TargetName =“r07” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.49998” To=“0”/>
  92.                             <DoubleAnimation Storyboard.TargetName =“r08” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.58331” To=“0”/>
  93.                             <DoubleAnimation Storyboard.TargetName =“r09” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.66664” To=“0”/>
  94.                             <DoubleAnimation Storyboard.TargetName =“r10” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.74997” To=“0”/>
  95.                             <DoubleAnimation Storyboard.TargetName =“r11” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.83330” To=“0”/>
  96.                             <DoubleAnimation Storyboard.TargetName =“r12” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.91663” To=“0”/>
  97.                         </Storyboard>
  98.                     </BeginStoryboard>
  99.                 </EventTrigger>
  100.             </Grid.Triggers>
  101.         </Grid>
  102.     </Grid >
  103. </Window>

后台代码:

[csharp] view plain copy

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Data;
  9. using System.Windows.Documents;
  10. using System.Windows.Input;
  11. using System.Windows.Media;
  12. using System.Windows.Media.Imaging;
  13. using System.Windows.Shapes;
  14. namespace Waiting
  15. {
  16.     /// <summary>
  17.     /// Window1.xaml 的交互逻辑
  18.     /// </summary>
  19.     public partial class Window1 : Window
  20.     {
  21.         public Window1()
  22.         {
  23.             InitializeComponent();
  24.         }
  25.         BackgroundWorker bgMeet;
  26.         private void button1_Click(object sender, RoutedEventArgs e)
  27.         {
  28.             bgMeet = new BackgroundWorker ();
  29.             //能否报告进度更新
  30.             bgMeet.WorkerReportsProgress = true;
  31.             //要执行的后台任务
  32.             bgMeet.DoWork += new DoWorkEventHandler (bgMeet_DoWork);
  33.             //进度报告方法
  34.             bgMeet.ProgressChanged += new ProgressChangedEventHandler (bgMeet_ProgressChanged);
  35.             //后台任务执行完成时调用的方法
  36.             bgMeet.RunWorkerCompleted += new RunWorkerCompletedEventHandler (bgMeet_RunWorkerCompleted);
  37.             bgMeet.RunWorkerAsync(); //任务启动
  38.         }
  39.         //执行任务
  40.         void bgMeet_DoWork(object sender, DoWorkEventArgs e)
  41.         {
  42.             //开始播放等待动画
  43.             this.Dispatcher.Invoke(new Action(() =>
  44.             {
  45.                 loading.Visibility = System.Windows. Visibility.Visible;
  46.             }));
  47.             //开始后台任务
  48.             GetData();
  49.         }
  50.         //报告任务进度
  51.         void bgMeet_ProgressChanged(object sender, ProgressChangedEventArgs e)
  52.         {
  53.             this.Dispatcher.Invoke(new Action(() =>
  54.             {
  55.                 this.lab_pro.Content = e.ProgressPercentage + “%”;
  56.             }));
  57.         }
  58.         //任务执行完成后更新状态
  59.         void bgMeet_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  60.         {
  61.             loading.Visibility = System.Windows. Visibility.Collapsed;
  62.             this.Dispatcher.Invoke(new Action(() =>
  63.             {
  64.                 this.lab_pro.Content = “完成” ;
  65.             }));
  66.         }
  67.         //模拟耗时任务
  68.         public void GetData()
  69.         {
  70.             for (int i = 0; i < 6; i++)
  71.             {
  72.                 bgMeet.ReportProgress(20*i);
  73.                 System.Threading. Thread.Sleep(400);
  74.             }
  75.         }
  76.     }
  77. }

WPF 设置TextBox为空时,背景为文字提示。

<TextBox FontSize="17" Height="26" Margin="230,150,189,0" Name="txt_Account" VerticalAlignment="Top" Foreground="Indigo" TabIndex="0" BorderThickness="1">
            <TextBox.Resources>
                <VisualBrush x:Key="HelpBrush" TileMode="None" Opacity="0.3" Stretch="None" AlignmentX="Left">
                    <VisualBrush.Visual>
                        <TextBlock FontStyle="Italic" Text="请输入用户名"/>
                    </VisualBrush.Visual>
                </VisualBrush>
            </TextBox.Resources>
            <TextBox.Style>
                <Style TargetType="TextBox">
                    <Style.Triggers>
                        <Trigger Property="Text" Value="{x:Null}">
                            <Setter Property="Background" Value="{StaticResource HelpBrush}"/>
                        </Trigger>
                        <Trigger Property="Text" Value="">
                            <Setter Property="Background" Value="{StaticResource HelpBrush}"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </TextBox.Style>
        </TextBox>

WPF中解决内存泄露的几点提示与解决方法

一直以来用WPF做一个项目,但是开发中途发现内存开销太大,用ANTS Memory Profiler分析时,发现在来回点几次载入页面的操作中,使得非托管内存部分开销从起始的43.59M一直到150M,而托管部分的开销也一直持高不下,即每次申请的内存在结束后不能完全释放。在网上找了不少资料,甚受益,现在修改后,再也不会出现这种现象了(或者说,即使有也不吓人),写下几个小心得:

1. 慎用WPF样式模板合并

我发现不采用合并时,非托管内存占用率较小,只是代码的理解能力较差了,不过我们还有文档大纲可以维护。

2. WPF样式模板请共享

共享的方式最简单不过的就是建立一个类库项目,把样式、图片、笔刷什么的,都扔进去,样式引用最好使用StaticResource,开销最小,但这样就导致了一些写作时的麻烦,即未定义样式,就不能引用样式,哪怕定义在后,引用在前都不行。

3. 慎用隐式类型var的弱引用

这个本来应该感觉没什么问题的,可是不明的是,在实践中,发现大量采用var与老老实实的使用类型声明的弱引用对比,总是产生一些不能正确回收的WeakRefrense(这点有待探讨,因为开销不是很大,可能存在一些手工编程的问题)

4. 写一个接口约束一下

谁申请谁释放,基本上这点能保证的话,内存基本上就能释放干净了。我是这么做的:

复制代码
    interface IUIElement : IDisposable
    {
        /// <summary>
        /// 注册事件
        /// </summary>
        void EventsRegistion();

        /// <summary>
        /// 解除事件注册
        /// </summary>
        void EventDeregistration();
    }
复制代码

在实现上可以这样:

复制代码
 1 #region IUIElement 成员
 2 public void EventsRegistion()
 3 {
 4     this.traineeReport.SelectionChanged += new SelectionChangedEventHandler(traineeReport_SelectionChanged);
 5 }
 6 
 7 public void EventDeregistration()
 8 {
 9     this.traineeReport.SelectionChanged -= new SelectionChangedEventHandler(traineeReport_SelectionChanged);
10 }
11 
12 private bool disposed;
13 
14 ~TraineePaymentMgr()
15 {
16     ConsoleEx.Log("{0}被销毁", this);
17     Dispose(false);
18 }
19 
20 public void Dispose()
21 {
22     ConsoleEx.Log("{0}被手动销毁", this);
23     Dispose(true);
24     GC.SuppressFinalize(this);
25 }
26 
27 protected void Dispose(bool disposing)
28 {
29     ConsoleEx.Log("{0}被自动销毁", this);
30     if(!disposed)
31     {
32         if(disposing)
33         {
34             //托管资源释放
35             ((IDisposable)traineeReport).Dispose();
36             ((IDisposable)traineePayment).Dispose();
37         }
38         //非托管资源释放
39     }
40     disposed = true;
41 }
42 #endregion
复制代码

比如写一个UserControl或是一个Page时,可以参考以上代码,实现这样接口,有利于资源释放。

5. 定时回收垃圾

复制代码
DispatcherTimer GCTimer = new DispatcherTimer();
public MainWindow()
{
    InitializeComponent();
    this.GCTimer.Interval = TimeSpan.FromMinutes(10); //垃圾释放定时器 我定为每十分钟释放一次,大家可根据需要修改
  this.GCTimer.start();

    this.EventsRegistion();    // 注册事件
}

public void EventsRegistion()
{
    this.GCTimer.Tick += new EventHandler(OnGarbageCollection);
}

public void EventDeregistration()
{
    this.GCTimer.Tick -= new EventHandler(OnGarbageCollection);
}

void OnGarbageCollection(object sender, EventArgs e)
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
}
复制代码

6. 较简单或可循环平铺的图片用GeometryDrawing实现

一个图片跟几行代码相比,哪个开销更少肯定不用多说了,而且这几行代码还可以BaseOn进行重用。

复制代码
<DrawingGroup x:Key="Diagonal_50px">
    <DrawingGroup.Children>
        <GeometryDrawing Brush="#FF2A2A2A" Geometry="F1 M 0,0L 50,0L 50,50L 0,50 Z"/>
        <GeometryDrawing Brush="#FF262626" Geometry="F1 M 50,0L 0,50L 0,25L 25,0L 50,0 Z"/>
        <GeometryDrawing Brush="#FF262626" Geometry="F1 M 50,25L 50,50L 25,50L 50,25 Z"/>
    </DrawingGroup.Children>
</DrawingGroup>
复制代码

这边是重用

<DrawingBrush x:Key="FrameListMenuArea_Brush" Stretch="Fill" TileMode="Tile" Viewport="0,0,50,50" ViewportUnits="Absolute"
Drawing="{StaticResource Diagonal_50px}"/>

上面几行代码相当于这个:

7. 使用Blend做样式的时候,一定要检查完成的代码

众所周知,Blend定义样式时,产生的垃圾代码还是比较多的,如果使用Blend,一定要检查生成的代码。

 

8. 静态方法返回诸如List<>等变量的,请使用out

比如

public static List<String> myMothod()

{...}

请改成

public static myMothod(out List<String> result)

{...}

 

9. 打针对此问题的微软补丁

3.5的应该都有了吧,这里附上NET4的内存泄露补丁地址,下载点这里 (QFE:  Hotfix request to implement hotfix KB981107 in .NET 4.0 )

这是官方给的说明,看来在样式和数据绑定部分下了点工夫啊:

  1. 运行一个包含样式或模板,请参阅通过使用 StaticResource 标记扩展或 DynamicResource 标记扩展应用程序资源的 WPF 应用程序。 创建使用这些样式或模板的多个控件。 但是,这些控件不使用引用的资源。 在这种情况的一些内存WeakReference对象和空间泄漏的控股数组后,垃圾回收释放该控件。
  2. 运行一个包含的控件的属性是数据绑定到的 WPF 应用程序DependencyObject对象。 该对象的生存期是超过控件的生存期。 许多控件时创建,一些内存WeakReference对象和容纳数组空格被泄漏后垃圾回收释放该控件。
  3. 运行使用树视图控件或控件派生于的 WPF 应用程序,选择器类。 将控件注册为控制中的键盘焦点的内部通知在KeyboardNavigation类。 该应用程序创建这些控件的很多。 例如对于您添加并删除这些控件。 在本例中为某些内存WeakReference对象和容纳数组空格被泄漏后垃圾回收释放该控件。

继续更新有关的三个8月补丁,详细的请百度:KB2487367  KB2539634  KB2539636,都是NET4的补丁,在发布程序的时候,把这些补丁全给客户安装了会好的多。

10.  对string怎么使用的建议

这个要解释话就长了,下面仅给个例子说明一下,具体的大家去找找MSDN

复制代码
        string ConcatString(params string[] items)
        {
            string result = "";
            foreach (string item in items)
            {
                result += item;
            }
            return result;
        }

        string ConcatString2(params string[] items)
        {
            StringBuilder result = new StringBuilder();
            for(int i=0, count = items.Count(); i<count; i++)
            {
                result.Append(items[i]);
            }
            return result.ToString();
        }
复制代码

建议在需要对string进行多次更改时(循环赋值、连接之类的),使用StringBuilder。我已经把工程里这种频繁且大量改动string的操作全部换成了StringBuilder了,用ANTS Memory Profiler分析效果显著,不仅提升了性能,而且垃圾也少了。

 

11. 其它用上的技术暂时还没想到,再补充…

 

如果严格按以上操作进行的话,可以得到一个满意的结果:

运行了三十分钟,不断的切换功能,然后休息5分钟,回头一看,结果才17M左右内存开销,效果显著吧。

然后对于调试信息的输出,我的做法是在窗体应用程序中附带一个控制台窗口,输出调试信息,给一个类,方便大家:

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Trainee.UI.UIHelper
{
    public struct COORD
    {
        public ushort X;
        public ushort Y;
    };

    public struct CONSOLE_FONT
    {
        public uint index;
        public COORD dim;
    };

    public static class ConsoleEx
    {
        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        internal static extern bool AllocConsole();

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        internal static extern bool SetConsoleFont(IntPtr consoleFont, uint index);

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        internal static extern bool GetConsoleFontInfo(IntPtr hOutput, byte bMaximize, uint count, [In, Out] CONSOLE_FONT[] consoleFont);

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        internal static extern uint GetNumberOfConsoleFonts();

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("kernel32", CharSet = CharSet.Auto)]
        internal static extern COORD GetConsoleFontSize(IntPtr HANDLE, uint DWORD);

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("kernel32.dll ")]
        internal static extern IntPtr GetStdHandle(int nStdHandle);

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern int GetConsoleTitle(String sb, int capacity);

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("user32.dll", EntryPoint = "UpdateWindow")]
        internal static extern int UpdateWindow(IntPtr hwnd);

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("user32.dll")]
        internal static extern IntPtr FindWindow(String sClassName, String sAppName);

        public static void OpenConsole()
        {
            var consoleTitle = "> Debug Console";
            AllocConsole();


            Console.BackgroundColor = ConsoleColor.Black;
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WindowWidth = 80;
            Console.CursorVisible = false;
            Console.Title = consoleTitle;
            Console.WriteLine("DEBUG CONSOLE WAIT OUTPUTING...{0} {1}\n", DateTime.Now.ToLongTimeString());

            try
            {
                //这里是改控制台字体大小的,可能会导致异常,在我这个项目中我懒得弄了,如果需要的的话把注释去掉就行了
                //IntPtr hwnd = FindWindow(null, consoleTitle);
                //IntPtr hOut = GetStdHandle(-11);

                //const uint MAX_FONTS = 40;
                //uint num_fonts = GetNumberOfConsoleFonts();
                //if (num_fonts > MAX_FONTS) num_fonts = MAX_FONTS;
                //CONSOLE_FONT[] fonts = new CONSOLE_FONT[MAX_FONTS];
                //GetConsoleFontInfo(hOut, 0, num_fonts, fonts);
                //for (var n = 7; n < num_fonts; ++n)
                //{
                //    //fonts[n].dim = GetConsoleFontSize(hOut, fonts[n].index);
                //    //if (fonts[n].dim.X == 106 && fonts[n].dim.Y == 33)
                //    //{
                //        SetConsoleFont(hOut, fonts[n].index);
                //        UpdateWindow(hwnd);
                //        return;
                //    //}
                //}
            }
            catch
            {

            }
        }

        public static void Log(String format, params object[] args)
        {
            Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] " + format, args);
        }
        public static void Log(Object arg)
        {
            Console.WriteLine(arg);
        }
    }
}
复制代码

在程序启动时,可以用ConsoleEx.OpenConsole()打开控制台,用ConsoleEx.Log(…..)或者干脆用Console.WriteLine进行输出就可以了。

WPF的TextBox产生内存泄露的情况

前段时间参与了一个WPF编写的项目,在该项目中有这样一个场景:在程序运行过程中需要动态地产生大量文本信息,并追加WPF界面上的一个TextBox的Text中进行显示。编写完之后,运行该项目的程序,发现在产生大量信息之后,发现系统变慢了,打开任务管理器才发现,该项目的程序占用了将近1.5G的内存(天啊!!!这不是一般的耗内存啊!!!)。后来通过查资料和探索才发现了WPF的TextBox在追加Text显示文本时会造成内存泄露。下面通过一个小Demo程序来展示一下这个内存泄露。

我的Demo程序很简单,就是在界面上显示一个TextBox和一个Button,点击Button后就从0到9999进行for循环并将这些数字追加的TextBox的Text中进行显示。代码如下,

<window x:Class="TextBoxMemoryLeak.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="测试TextBox内存泄露" Height="350" Width="525"
        WindowStartupLocation="CenterScreen">
    <grid Margin="5">
        </grid><grid .RowDefinitions>
            <rowdefinition Height="*"></rowdefinition>
            <rowdefinition Height="35"></rowdefinition>
        </grid>
        <dockpanel Grid.Row="0">
            <textbox Name="tbOutput" IsReadOnly="True" VerticalScrollBarVisibility="Auto"></textbox>
        </dockpanel>
        <stackpanel Grid.Row="1"
                    FlowDirection="RightToLeft"
                    Orientation="Horizontal">
            <button Name="btnStart" Content="开 始" Margin="5,4,5,4" Width="65" Click="btnStart_Click"></button>
        </stackpanel>
    
</window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TextBoxMemoryLeak
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            this.btnStart.IsEnabled = false;
            this.tbOutput.Text = "";

            for (int i = 0; i < 10000; i++)
            {
                //使用此语句进行Textbox的追加会造成内存泄露
                //this.tbOutput.Text += string.Format("{0}\n", i);

                //使用此语句进行Textbox的追加可避免内存泄露
                this.tbOutput.AppendText(string.Format("{0}\n", i));
            }

            this.btnStart.IsEnabled = true;
        }
    }
}

界面如下所示:

内存泄露情况

最初我们采用的是TextBox的Text追加方式如下

this.tbOutput.Text += string.Format("{0}\n", i);

构建,启动调试后,我们查看任务管理器,此时所占内存只有16M,

点击【开始】按钮之后,等到从0输出到9999之后,我们再查看任务管理器,发现此时所占的内存飙到了600+M,

若此时再点击【开始】按钮,等循环结束,发现所占内存飙到了900+M,

再点击【开始】按钮的话,就要发生OutOfMemory异常的。当我们将循环改为从0到19999时,第一次点击【开始】按钮,我的机器就发生OutOfMemory异常了。

避免内存泄露的情况

将TextBox的Text追加方式改为下面语句

this.tbOutput.AppendText(string.Format("{0}\n", i));

构建,启动调试,然后点击界面的【开始】按钮,等循环结束,我们查看任务管理器,测试Demo程序只占了29M内存(此时是从0到19999的循环)。

 

 

 

TextBox存在内存泄露的可能

背景

-WPF桌面程序中,增加了一个TextBox控件,用于显示输出的日志信息,日志信息量很大(具体数值未统计)

XAML 代码
 <TextBox Text="{Binding Message}" TextWrapping="Wrap" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" TextChanged="TextBox_TextChanged" />

CS 代码
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (!(sender is TextBox)) return;
        var tb = (TextBox)sender;
        if (tb.Text.Length > 30000) //textbox maxlength = 32767
      {
        tb.Text = "Auto Clear Histroy Information\n\r";
      }
       tb.Select(tb.Text.Length, 0);
    }

问题

运行一段时间后操作系统弹出信息,称“XXX程序出问题了”(截图后补吧)。在增加日志显示功能前,此异常未出现。

解决

参考:Google搜索到的信息
怀疑是日志信息太多,造成程序内存泄露。TextBox控件支持回退功能(Undo),但是回退功能需要占用更多内存。

设置 
     UndoLimit="0"  或者 IsUndoEnabled="False"
 可关闭Undo功能

stay connected

228,480

Fans

21,563

Followers

20,563

Followers

8,125

Subscribers

2,253

Subscribers

10,563

Followers

最新文章

entertainment
Buckingham Palace soil used in Tate exhibit
Oct 13, 2016
Sports
Legendary coach Steve Spurrier was truly
Oct 13, 2016
Technology
Acer reveals all-in-one Windows 10 PC
Oct 13, 2016
entertainment
Revival allows Selena Gomez to shed
Oct 13, 2016
entertainment
Buckingham Palace soil used in Tate exhibit
Oct 13, 2016
Sports
Legendary coach Steve Spurrier was truly
Oct 13, 2016
Technology
Acer reveals all-in-one Windows 10 PC
Oct 13, 2016
entertainment
Revival allows Selena Gomez to shed
Oct 13, 2016