年度巨献-WPF项目开发过程当中WPF小知识点汇总(原创+摘抄)

WPF中Style的使用

  Styel在英文中解释为”样式“,在Web开发中,css为层叠样式表,自从.net3.0推出WPF以来,WPF也有样式一说,经过设置样式,使其WPF控件外观更加美化同时减小了大量的复杂属性的设置。css

   在WPF中,设置外观样式咱们有不少种方式,好比经过设置控件的属性来控制控件的外观样式;或者经过在每个控件中分别设置Style;或者经过在整个Window.Resource中设置Style,又或者在App.xaml的Application.Resource设置Style。html

   在此咱们就不讨论第一种方式设置控件的外观了,由于这不涉及到Style的使用。那么后三种设置样式来控制控件的外观有什么区别呢?那么咱们来分别讨论吧!linux

   第一,经过在每个控件中分别设置Style来控制控件的外观,示例代码以下:git

 

            <Button Content="Button" Height="23" Name="button3" Width="75">
                <Button.Style>
                    <Style TargetType="Button">
                        <Setter Property="Background" Value="Black" />
                    </Style>
                </Button.Style>
            </Button>程序员

以上样式的设置只正对当前的Button有效,与其余同种类型的控件无关。github

  第二,经过在Window.Resource中设置Style来控制控件的外观,示例代码以下:数据库

 

    <Window.Resources>
        <Style TargetType="Button">
            <Setter Property="Background" Value="LightBlue" />
        </Style>
    </Window.Resources>express

以上样式的设置,针对整个Window的全部Button有效(只要没有单独的对Button设置),这种方法呢,相对于第一种来讲减小了代码量。同时修改起来出错的可能性较小!编程

第三,经过在App.xaml中的Application.Resource中设置Style来控制控件的外观,示例代码以下:canvas

<Application.Resource>

<Style TargetType="Button">
            <Setter Property="FontFamily" Value="MS Reference Sans Serif" />
            <Setter Property="Background">
                <Setter.Value>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                        <GradientStop Color="White" Offset="0"/>
                        <GradientStop Color="SkyBlue" Offset="0.2"/>
                        <GradientStop Color="SkyBlue" Offset="0.8"/>
                        <GradientStop Color="White" Offset="1"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>

</Application.Resource>
View Code

 

以上样式的设置,针对整个Application的全部Button有效(只要在Window.Resource或者独立的Button中没有分别设置),这种方法来设置控件的外观样式呢,相对于前两种来讲,代码量有大幅的增长,同时呢,在一个应用程序中,每每同种类型的控件的不少属性都是相同的,咱们在Applicaiton.Resource进行全局设置,使其维护更加方便!

  好了,以上是我对WPF中Style的理解,但愿在我总结的同时,可以给同行们提供帮助,若是发现错误,请积极指正,谢谢!

WPF ListBox 横向排列(摘抄)

 

若是只是单纯的让ListBox能够横向配列,这样很简单,只须要更改ListBox的ItemsPanel模板就能够,例如:

<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation=”Horizontal” IsItemsHost=”True”/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
  可是这样的修改,ListBox只能横向排列,不会根据宽度自动换行,若是想要横向排列的ListBox支持根据宽度自动换行的话,须要这样写:

<ListBox.Template>
<ControlTemplate TargetType=”{x:Type ListBox}”>
<ScrollViewer HorizontalScrollBarVisibility=”Disabled” VerticalScrollBarVisibility=”Auto”>
<WrapPanel Orientation=”Horizontal” IsItemsHost=”True” ScrollViewer.CanContentScroll=”True”/>
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
 
<Style TargetType="{x:Type ListBox}">
  <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
  <Setter Property="ItemsPanel">
    <Setter.Value>
      <ItemsPanelTemplate>
        <WrapPanel Orientation="Horizontal" Margin="2" Background="LightGray"/>
      </ItemsPanelTemplate>
    </Setter.Value>
  </Setter>
  <Setter Property="ItemContainerStyle">
    <Setter.Value>
      <Style TargetType="ListBoxItem">
        <Style.Triggers>
          <Trigger Property="IsSelected" Value="True">
            <Trigger.Setters>
              <Setter Property="BorderThickness" Value="1"/>
              <Setter Property="BorderBrush" Value="Red"/>
            </Trigger.Setters>
          </Trigger>
        </Style.Triggers>
      </Style>
    </Setter.Value>
  </Setter>
</Style>
View Code

 

截取字符串总结

string str="123abc456";
int i=3;
1 取字符串的前i个字符
   str=str.Substring(0,i); // or str=str.Remove(i,str.Length-i);
2 去掉字符串的前i个字符:
   str=str.Remove(0,i); // or str=str.Substring(i);
3 从右边开始取i个字符:
  str=str.Substring(str.Length-i); // or str=str.Remove(0,str.Length-i);
4 从右边开始去掉i个字符:
   str=str.Substring(0,str.Length-i); // or str=str.Remove(str.Length-i,i);
5 判断字符串中是否有"abc" 有则去掉之
   using System.Text.RegularExpressions;
   string str = "123abc456";
   string a="abc";
   Regex r = new Regex(a);
   Match m = r.Match(str);
   if (m.Success)
   {
    //下面两个取一种便可。
      str=str.Replace(a,"");
      Response.Write(str);
      string str1,str2;
      str1=str.Substring(0,m.Index);
      str2=str.Substring(m.Index+a.Length,str.Length-a.Length-m.Index);
      Response.Write(str1+str2);
   }
6 若是字符串中有"abc"则替换成"ABC"
   str=str.Replace("abc","ABC");
 
************************************************
 
string str="adcdef"; int indexStart = str.IndexOf("d");
 
int endIndex =str.IndexOf("e");
 
string toStr = str.SubString(indexStart,endIndex-indexStart);
 
c#截取字符串最后一个字符的问题!
 
str1.Substring(str1.LastIndexOf(",")+1);
 
C# 截取字符串最后一个字符
 
k = k.Substring(k.Length-1, 1);
View Code

 

wpf移动图片变大

 
<Grid>
        <Canvas x:Name="LayoutRoot">
            <Image Cursor="Hand" MouseLeftButtonDown="imgLogo1_MouseLeftButtonDown" MouseEnter="imgLogo1_MouseEnter"
                   MouseLeave="imgLogo1_MouseLeave" Canvas.ZIndex="1" x:Name="imgLogo1" Canvas.Left="100"
                   Canvas.Top="60" Height="100" Source="Image/Picture.jpg">
                <Image.RenderTransform>
                    <ScaleTransform x:Name="LogoScale" CenterX="90" CenterY="96">
 
                    </ScaleTransform>
                </Image.RenderTransform>
            </Image>
        </Canvas>
    </Grid>
  
 
public partial class Window8 : Window
   {
       public Window8()
       {
           InitializeComponent();
           timer = new System.Windows.Threading.DispatcherTimer();
           timer.Interval = TimeSpan.FromMilliseconds(50);
           timer.Tick += new EventHandler(timer_Tick);
       }
 
       private System.Windows.Threading.DispatcherTimer timer;
       private ScaleDirection scaleDirection ;
       
 
       void timer_Tick(object sender, EventArgs e)
       {
           AdjustScale(scaleDirection, LogoScale);
       }
 
       void AdjustScale(ScaleDirection scaleDirection, ScaleTransform scale)
       {
           if (scaleDirection == ScaleDirection.Down)
           {
               if (scale.ScaleX < 1.3)
               {
                   scale.ScaleX += 0.05; scale.ScaleY += 0.05;
               }
               else
                   timer.Stop();
           }
           else
           {
               if (scale.ScaleX > 1.0)
               {
                   scale.ScaleX -= 0.05;
                   scale.ScaleY -= 0.05;
               }
               else
                   timer.Stop();
           }
       }
 
       enum ScaleDirection
       {
           Up,
           Down
       }
 
       private void imgLogo1_MouseEnter(object sender, MouseEventArgs e)
       {
           scaleDirection = ScaleDirection.Down;
           timer.Start();
       }
 
       private void imgLogo1_MouseLeave(object sender, MouseEventArgs e)
       {
           scaleDirection = ScaleDirection.Up;
           timer.Start();
       }
 
       private void imgLogo1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
       {
           MessageBox.Show("test");
       }
   }
View Code

 

WPF的windows Triggers

<Window.Triggers>
        <EventTrigger SourceName="CrazyButton"
                      RoutedEvent="Window.Loaded">
            <!--<EventTrigger.Actions>-->
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="image2"
                                         Storyboard.TargetProperty="Opacity"
                                         To="0"
                                         Duration="0:0:5"
                                         AutoReverse="False"
                                         RepeatBehavior="Forever">
                            </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="image2"
                                         Storyboard.TargetProperty="(Canvas.Left)"
                                         To="500"
                                         Duration="0:0:5"
                                         AutoReverse="False"
                                         RepeatBehavior="Forever"/>
                    </Storyboard>
                </BeginStoryboard>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="image1"
                                         Storyboard.TargetProperty="(Canvas.Left)"
                                         To="500"
                                         Duration="0:0:5"
                                         AutoReverse="False"
                                         RepeatBehavior="Forever"/>
                    </Storyboard>
                </BeginStoryboard>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="image1"
                                         Storyboard.TargetProperty="Opacity"
                                         To="1"
                                         Duration="0:0:5"
                                         AutoReverse="False"
                                         RepeatBehavior="Forever">
                        </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            <!--</EventTrigger.Actions>-->
        </EventTrigger>
    </Window.Triggers>
View Code

 

WPF的Converter

1.建类,必须继承IValueConverter接口,在命名空间System.Windows.Data下
    class BoolToContentConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool temp = bool.Parse(value.ToString());
            if (temp)
                return "暂 停";
            else
                return "开 始";
        }
 
 
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
 
 
    }
 
2.在资源文件xaml里加入 转换器,converters为命名空间定义 好比xmlns:converters="clr-namespace:Converters"
 <converters:BoolToContentConverter x:Key="BoolToContentConverter"/>
 
3.使用转换器
<Button Content="{Binding Path=isDownloading, Converter={StaticResource BoolToContentConverter}}" > </Button>
 
4,带参数的Converter
<TextBox  Text="{Binding XXXX,Converter={StaticResource UsrConverter},

  ConverterParameter={StaticResource userList }}" /> 
View Code

 

Converter 里对参数的使用(摘抄) 

public class UserNameConverter : IValueConverter

{

    public object IValueConverter.Convert(object value, Type targetType,object parameter, CultureInfo culture)

    {

      List<User> usrs = parameter as List<User>;

      ...

    }

}
View Code

 

如何学好wpf(摘抄)


用了三年多的WPF,开发了不少个WPF的项目,就我本身的经验,谈一谈如何学好WPF,固然,抛砖引玉,若是您有什么建议也但愿不吝赐教。

  WPF,全名是Windows Presentation Foundation,是微软在.net3.0 WinFX中提出的。WPF是对Direct3D的托管封装,它的图形表现依赖于显卡。固然,做为一种更高层次的封装,对于硬件自己不支持的一些图形特效的硬实现,WPF提供了利用CPU进行计算的软实现,用以简化开发人员的工做。

  简单的介绍了一下WPF,这方面的资料也有不少。做于微软力推的技术,整个推行也符合微软一向的风格。简单,易用,强大,外加几个创新概念的噱头。

  噱头一:声明式编程。从理论上讲,这个不算什么创新。Web界面声明式开发早已如火如荼了,这种界面层的声明式开发也是大势所趋。为了适应声明式编程,微软推出了XAML,一种扩展的XML语言,而且在.NET 3.0中加入了XAML的编译器和运行时解析器。XAML加上IDE强大的智能感知,确实大大方便了界面的描述,这点是值得确定的。

  噱头二:紧接着,微软借XAML描绘了一副更为美好的图片,界面设计者和代码开发者能够并行的工做,二者经过XAML进行交互,实现设计和实现的分离。不得不说,这个想法很是打动人心。以往设计人员大可能是经过photoshop编辑出来的图片来和开发人员进行交互的,须要开发人员根据图片的样式来进行转换,以生成实际的效果。既然有了这层转换,因此最终出来的效果和设计时总会有误差,因此不少时候开发人员不得不忍受设计人员的抱怨。WPF的出现给开发人员看到了一线曙光,我只负责逻辑代码,UI你本身去搞,一结合就能够了,不错。可实际开发中,这里又出现了问题,UI的XAML部分能彻底丢给设计人员么?

  这个话题展开可能有点长,微软提供了Expression Studio套装来支持用工具生成XAML。那么这套工具是否可以知足设计人员的须要呢?通过和不少设计人员和开发人员的配合,最常听到的话相似于这样。“这个没有Photoshop好用,会限制个人灵感”, “他们生成的XAML太糟糕了...”。确实,在同一项目中,设计人员使用Blend进行设计,开发人员用VS来开发代码逻辑,这个想法稍有理想化: 
  · 有些UI效果是很难或者不能够用XAML来描述的,须要手动编写效果。 
  · 大多数设计人员很难接受面向对象思惟,包括对资源(Resource)的复用也不理想 
  · 用Blend生成的XAML代码并不高效,一种很简单的布局也可能被翻译成很冗长的XAML。

  在经历过这样不愉快的配合后,不少公司引入了一个integrator的概念。专门抽出一个比较有经验的开发人员,负责把设计人员提供的XAML代码整理成比较符合要求的XAML,而且在设计人员没法实现XAML的状况下,根据设计人员的须要来编写XAML或者手动编写代码。关于这方面,个人经验是,设计人员放弃Blend,使用Expression Design。Design工具仍是比较符合设计人员的思惟,固然,须要特别注意一些像素对齐方面的小问题。开发人员再经过设计人员提供的design文件转化到项目中。这里通常是用Blend打开工程,Expression系列复制粘贴是格式化到剪切板的,因此能够在design文件中选中某一个图形,点复制,再切到blend对应的父节点下点粘贴,适当修改一下转化过来的效果。

  做为一个矢量化图形工具,Expression Studio确实给咱们提供了不少帮助,也能够达到设计人员同开发人员进行合做,不过,不像微软描述的那样天然。总的来讲,还好,可是不够好。

  这里,要步入本篇文章的重点了,也是我不少时候听起来很无奈的事情。微软在宣传WPF时过于宣传XAML和工具的简易性了,形成不少刚接触WPF的朋友们会产生这样一副想法。WPF=XAML? 哦,相似HTML的玩意...

  这个是不对的,或者是不能这么说的。做为一款新的图形引擎,以Foundation做为后缀,表明了微软的野心。借助于托管平台的支持,微软寄但愿WPF打破长久以来桌面开发和Web开发的壁垒。固然,因为须要.net3.0+版本的支持,XBAP已经逐渐被Silverlight所取替。在整个WPF的设计里,XAML(Markup)确实是他的亮点,也吸收了Web开发的精华。XAML对于帮助UI和实现的分离,有如如虎添翼。但XAML并非WPF独有的,包括WF等其余技术也在使用它,若是你愿意,全部的UI你也能够完成用后台代码来实现。正是为了说明这个概念,Petzold在Application = codes + markup 一书中一分为二,前半本书彻底使用Code来实现的,后面才讲解了XAML以及在XAML中声明UI等。但这本书叫好不叫座,你有必定开发经验回头来看发现条条是路,很是经典,但你抱着这本书入门的话估计你可能就会一头雾水了。

  因此不少朋友来抱怨,WPF的学习太曲折了,上手很容易,但是深刻一些就比较困难,常常碰到一些诡异困难的问题,最后只能推到不能作,不支持。复杂是由数量级别决定的,这里借LearnWPF的一些数据,来对比一下Asp.net, WinForm和WPF 类型以及类的数量:

ASP.NET 2.0

WinForms 2.0

WPF

 

1098 public types

1551 classes

777 public types

1500 classes

1577 public types

3592 classes

  固然,这个数字未必准确,也不能由此说明WPF相比Asp.net、WinForm,有多复杂。可是面对如此庞大的类库,想要作到一览众山小也是很困难的。想要搞定一个你们伙,咱们就要把握它的脉络,所谓庖丁解牛,也须要知道在哪下刀。在正式谈如何学好WPF以前,我想和朋友们谈一下如何学好一门新技术。

  学习新技术有不少种途经,自学,培训等等。相对于咱们来讲,据说一门新技术,引发咱们的兴趣,查询相关讲解的书籍(资料),边看书边动手写写Sample这种方式应该算最多见的。那么怎么样才算学好了,怎么样才算是学会了呢?在这里,解释下知识树的概念:

  这不是什么创造性的概念,也不想就此谈大。我感受学习主要是两方面的事情,一方面是向内,一方面是向外。这棵所谓树的底层就是一些基础,固然,只是个举例,具体图中是否是这样的逻辑就不要见怪了。学习,就是一个不断丰富本身知识树的过程,咱们一方面在努力的学习新东西,为它添枝加叶;另外一方面,也会不停的思考,理清脉络。这里谈一下向内的概念,并非没有学会底层一些的东西,上面的东西就全是空中楼阁了。不多有一门技术是仅仅从一方面发展来的,就是说它确定不是只有一个根的。比方说没有学过IL,我并不认为.NET就没法学好,你能够从另一个根,从相对高一些的抽象上来理解它。可是对底层,对这种关键的根,学一学它仍是有助于咱们理解的。这里个人感受是,向内的探索是无止境的,向外的扩展是无限可能的。

  介绍了这个,接下来细谈一下如何学好一门新技术,也就是如何添砖加瓦。学习一门技术,就像新new了一个对象,你对它有了个大体了解,但它是游离在你的知识树以外的,你要作的很重要的一步就是把它连好。固然这层向内的链接不是一夕之功,可能会连错,可能会少连。我对学好的理解是要从外到内,再从内到外,就读书的例子谈一下这个过程:

  市面关于技术的书不少,名字也五花八门的,简单的整理一下,分为三类,就叫V1,V2,V3吧。 
· V1类,名字通常比较好认,相似30天学通XXX,一步一步XXX…没错,入门类书。这种书大体上都是以展现为主的,一个一个Sample,一步一步的带你过一下整个技术。大多数咱们学习也都是从这开始的,倒杯茶水,打开电子书,再打开VS,敲敲代码,只要注意力集中些,基本不会跟不上。学完这一步,你应该对这门技术有了必定的了解,固然,你脑海中确定不自觉的为这个向内连了不少线,固然不必定正确,不过这个新东东的建立已经有轮廓了,咱们说,已经达到了从外的目的。 
· V2类,名字就比较乱了,其实意思差很少,只是用的词语不同。这类有深刻解析XXX,XXX本质论…这种书参差不齐,有些明明是入门类书非要换个马甲。这类书主要是详细的讲一下书中的各个Feature, 前因后果,帮你更好的认识这门技术。若是你是带着问题去的,大多数也会帮你理清,书中也会顺带提一下这个技术的来源,帮你更好的把请脉络。这种书是能够看出做者的功力的,是否是真正达到了深刻浅出。这个过程结束,咱们说,已经达到了从外到内的目的。 
· V3类,若是你认真,踏实的走过了前两个阶段,我以为在简历上写个精通也不为过。这里提到V3,其实名字上和V2也差很少。往内走的越深,越有种冲动想把这东西搞透,就像被强行注入了内力,虽然和体内脉络已经和谐了,不过总该本身试试怎么流转吧。这里谈到的就是由内向外的过程,第一本给我留下深入印象的书就是侯捷老师的深刻浅出MFC,在这本书中,侯捷老师从零开始,一步一步的构建起了整个类MFC的框架结构。书读两遍,如醍醐灌顶,痛快淋漓。若是朋友们有这种有思想,讲思想,有匠心的书也但愿多多推荐,共同进步。

  回过头,就这个说一下WPF。WPF如今的书也有很多,入门的书我首推MSDN。其实我以为做为入门类的书,微软的介绍就已经很好了,面面俱到,用词准确,Sample附带的也不错。再往下走,好比Sams.Windows.Presentation.Foundation.Unleashed或者Apress_Pro_WPF_Windows_Presentation_Foundation_in_NET_3_0也都很是不错。这里没有看到太深刻的文章,偶有深刻的也都是一笔带过,或者是直接用Reflector展现一下Code。

  接下来,谈一下WPF的一些Feature。由于工做关系,常常要给同事们培训讲解WPF,愈来愈发现,学好学懂未必能讲懂讲透,慢慢才体会到,这是一个插入点的问题。你们的水平良莠不齐,也就是所谓的总口难调,那么讲解的插入点就决定了这个讲解可否有一个好的效果,这个插入点必定要尽量多的插入到你们的知识树上去。最开始的插入点是你们比较熟悉的部分,那么日后的讲解就能一气通贯,反之就是一个接一个的概念,也就是最讨厌的用概念讲概念,搞得人一头雾水。

  首先说一下Dependency Property(DP)。这个也有不少人讲过了,包括我也常常和人讲起。讲它的储存,属性的继承,验证和强制值,反射和值储存的优先级等。那么为何要有DP,它能带来什么好处呢?

  抛开DP,先说一下Property,属性,用来封装类的数据的。那么DP,翻译过来是依赖属性,也就是说类的属性要存在依赖,那么这层依赖是怎么来的呢。任意的一个DP,MSDN上的一个实践是这样的:

public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register("IsSpinning", typeof(bool)); 

public bool IsSpinning 
{ 
get { return (bool)GetValue(IsSpinningProperty); } 
set { SetValue(IsSpinningProperty, value); } 
} 
View Code

  单看IsSpinning,和传统的属性没什么区别,类型是bool型,有get,set方法。只不过内部的实现分别调用了GetValue和SetValue,这两个方法是DependecyObject(简称DO,是WPF中全部可视Visual的基类)暴露出来的,传入的参数是IsSpinningProperty。再看IsSpinningProperty,类型就是DependencyProperty,前面用了static readonly,一个单例模式,有DependencyProperty.Register,看起来像是往容器里注册。

  粗略一看,也就是如此。那么,它真正的创新、威力在哪里呢。抛开它精巧的设计不说,先看储存。DP中的数据也是存储在对象中的,每一个DependencyObject内部维护了一个EffectiveValueEntry的数组,这个EffectiveValueEntry是一个结构,封装了一个DependencyProerty的各个状态值animatedValue(做动画),baseValue(原始值),coercedValue(强制值),expressionValue(表达式值)。咱们使用DenpendencyObject.GetValue(IsSpinningProperty)时,就首先取到了该DP对应的EffectiveValueEntry,而后返回当前的Value。

  那么,它和传统属性的区别在哪里,为何要搞出这样一个DP呢?第一,内存使用量。咱们设计控件,不可避免的要设计不少控件的属性,高度,宽度等等,这样就会有大量(私有)字段的存在,一个继承树下来,低端的对象会没法避免的膨胀。而外部经过GetValue,SetValue暴露属性,内部维护这样一个EffectiveValueEntry的数组,顾名思义,只是维护了一个有效的、设置过值的列表,能够减小内存的使用量。第二,传统属性的局限性,这个有不少,包括一个属性只能设置一个值,不能获得变化的通知,没法为现有的类添加新的属性等等。

  这里谈谈DP的动态性,表现有二:能够为类A设置类B的属性;能够给类A添加新的属性。这都是传统属性所不具有的,那么是什么让DependencyObject具备这样的能力呢,就是这个DenpencyProperty的设计。在DP的设计中,对于单个的DP来讲,是单例模式,也就是构造函数私有,咱们调用DependencyProperty.Register或者DependencyProperty.RegisterAttached这些静态函数的时候,内部就会调用到私有的DP 的构造函数,构建出新的DP,并把这个DP加入到全局静态的一个HashTable中,键值就是根据传入时的名字和对象类型的hashcode取异或生成的。

  既然DependencyProperty是维护在一个全局的HashTable中的,那么具体到每一个对象的属性又是怎么经过GetValue和SetValue来和DependencyProperty关联上的,并得到PropertyChangeCallback等等的能力呢。在一个DP的注册方法中,最多传递五个参数 :

public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata,

ValidateValueCallback validateValueCallback);

其中第一和第三个参数就是用来肯定HashTable中的键值,第二个参数肯定了属性的类型,第四个参数是DP中的重点,定义了DP的属性元数据。在元数据中,定义了属性变化和强制值的Callback等。那么在一个SetValue的过程当中,会出现哪些步骤呢:

  1. 取得该DP下对应这个DependencyObject的PropertyMetadata,这句可能听起来有些拗口。Metadata,按微软通常的命名规则,通常是用来描述对象自身数据的,那么一个DP是否只含有一个propertyMetadata呢?答案是否是,一个DP内部维护了一个比较高效的map,里面存储了多个propertyMetadata,也就是说DP和propertyMetadata是一对多的关系。这里是为何呢,由于同一个DP可能会被用到不一样的DependencyObject中去,对于每类DependencyObject,对这个DP的处理都有所不一样,这个不一样能够表如今默认值不一样,properyMetadata里面的功能不一样等等,因此在设计DP的时候设计了这样一个DP和propertyMetadata一对多的关系。
  2. 取得了该DP下对应真正干活的PropertyMetadata,下一步要真正的”SetValue”了。这个“value”就是要设置的值,设置以后要保存到咱们前面提到的EffectiveValueEntry上,因此这里还要先取得这个DP对应的EffectiveValueEntry。在DependencyObject内部的EffectiveValueEntry的数组里面查找这个EffectiveValueEntry,有,取得;没有,新建,加入到数组中。
  3. 那么这个EffectiveValueEntry究竟是用来干什么的,为何须要这样一个结构体?若是你对WPF有必定了解,可能会据说WPF值储存的优先级,local value>style trigger>template trigger>…。在一个EffectiveValueEntry中,定义了一个BaseValueSourceInternal,用来表示设置当前Value的优先级,当你用新的EffectiveValueEntry更新原有的EffectiveValueEntry时,若是新的EffectiveValueEntry中BaseValueSourceInternal高于老的,设置成功,不然,不予设置。
  4. 剩下的就是proertyMetadata了,当你使用相似以下的参数注册DP,
public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register( 
"CurrentReading", 
typeof(double), 
typeof(Gauge), 
new FrameworkPropertyMetadata( 
Double.NaN, 
FrameworkPropertyMetadataOptions.AffectsMeasure, 
new PropertyChangedCallback(OnCurrentReadingChanged), 
new CoerceValueCallback(CoerceCurrentReading)), new ValidateValueCallback(IsValidReading));
View Code

  当属性发生变化的时候,就会调用metadata中传入的委托函数。这个过程是这样的, DependencyObject中定义一个虚函数 :

protected virtual void OnPropertyChanged(DependencyPropertyChangedEventArgs e)。

  当DP发生变化的时候就会先首先调用到这个OnPropertyChanged函数,而后若是metaData中设置了PropertyChangedCallback的委托,再调用委托函数。这里咱们设置了FrameworkPropertyMetadataOptions.AffectsMeasure, 意思是这个DP变化的时候须要从新测量控件和子控件的Size。具体WPF的实现就是FrameworkElement这个类重载了父类DependencyObject的OnPropertyChanged方法,在这个方法中判断参数中的metadata是不是FrameworkPropertyMetadata,是否设置了
FrameworkPropertyMetadataOptions.AffectsMeasure这个标志位,若是有的话调用一下自身的InvalidateMeasure函数。

  简要的谈了一下DependencyProperty,除了微软那种自卖自诩,这个DependencyProperty究竟为咱们设计实现带来了哪些好处呢? 
  1. 就是DP自己带有的PropertyChangeCallback等等,方便咱们的使用。 
  2. DP的动态性,也能够叫作灵活性。举个例子,传统的属性,须要在设计类的时候设计好,你在汽车里拿飞机翅膀确定是不能够的。但是DependencyObject,经过GetValue和SetValue来模仿属性,相对于每一个DependencyObject内部有一个百宝囊,你能够随时往里放置数据,须要的时候又能够取出来。固然,前面的例子都是使用一个传统的CLR属性来封装了DP,看起来和传统属性同样须要声明,下面介绍一下WPF中很强大的Attached Property。

  再谈Attached Property以前,我打算和朋友们谈一个设计模式,结合项目实际,会更有助于分析DP,这就是MVVM(Mode-View-ViewModel)。关于这个模式,网上也有不少论述,也是我常用的一个模式。那么,它有什么特色,又有什么优缺点呢?先来看一个模式应用:

public class NameObject : INotifyPropertyChanged 
{ 
private string _name = "name1"; 
public string Name 
{ 
get 
{ 
return _name; 
} 
set 
{ 
_name = value; 
NotifyPropertyChanged("Name"); 
} 
} 

private void NotifyPropertyChanged(string name) 
{ 
if (PropertyChanged != null) 
{ 
PropertyChanged(this, new PropertyChangedEventArgs(name)); 
} 
} 

public event PropertyChangedEventHandler PropertyChanged; 
} 



public class NameObjectViewModel : INotifyPropertyChanged 
{ 

private readonly NameObject _model; 

public NameObjectViewModel(NameObject model) 
{ 
_model = model; 
_model.PropertyChanged += new PropertyChangedEventHandler(_model_PropertyChanged); 
} 

void _model_PropertyChanged(object sender, PropertyChangedEventArgs e) 
{ 
NotifyPropertyChanged(e.PropertyName); 
} 

public ICommand ChangeNameCommand 
{ 
get 
{ 
return new RelayCommand( 
new Action<object>((obj) => 
{ 

Name = "name2"; 

}), 
new Predicate<object>((obj) => 
{ 
return true; 
})); 
} 
} 

public string Name 
{ 
get 
{ 
return _model.Name; 
} 
set 
{ 
_model.Name = value; 
} 
} 

private void NotifyPropertyChanged(string name) 
{ 
if (PropertyChanged != null) 
{ 
PropertyChanged(this, new PropertyChangedEventArgs(name)); 
} 
} 

public event PropertyChangedEventHandler PropertyChanged; 
} 



public class RelayCommand : ICommand 
{ 
readonly Action<object> _execute; 
readonly Predicate<object> _canExecute; 

public RelayCommand(Action<object> execute, Predicate<object> canExecute) 
{ 
_execute = execute; 
_canExecute = canExecute; 
} 

public bool CanExecute(object parameter) 
{ 
return _canExecute == null ? true : _canExecute(parameter); 
} 

public event EventHandler CanExecuteChanged 
{ 
add { CommandManager.RequerySuggested += value; } 
remove { CommandManager.RequerySuggested -= value; } 
} 

public void Execute(object parameter) 
{ 
_execute(parameter); 
} 
} 



public partial class Window1 : Window 
{ 
public Window1() 
{ 
InitializeComponent(); 
this.DataContext = new NameObjectViewModel(new NameObject()); 
} 
} 



<Window x:Class="WpfApplication7.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
Title="Window1" Height="300" Width="300"> 
<Grid> 
<TextBlock Margin="29,45,129,0" Name="textBlock1" Height="21" VerticalAlignment="Top" 
Text="{Binding Path=Name}"/> 
<Button Height="23" Margin="76,0,128,46" Name="button1" VerticalAlignment="Bottom" 
Command="{Binding Path=ChangeNameCommand}">Rename</Button> 
</Grid> 
</Window
View Code

类的关系如图所示:

  这里NameObject -> Model,NameObjectViewModel -> ViewModel,Window1 -> View咱们知道,在一般的Model-View世界中,不管MVC也好,MVP也好,包括咱们如今提到的MVVM,它的Model和View的功能都相似,Model是用来封装核心数据,逻辑与功能计算的模型,View是视图,具体能够对应到窗体(控件)等。那么View的功能主要有,把Model的数据显示出来,响应用户的操做,修改Model,剩下Controller或Presenter的功能就是要组织Model和View之间的关系,整理一下Model-View世界中的需求点,大体有: 
  1. 为View提供数据,如何把Model中的数据提供给View。 
  2. Model中的数据发生变化后,View如何更新视图。 
  3. 根据不一样的状况为Model选择不一样的View。 
  4. 如何响应用户的操做,鼠标点击或者一些其余的事件,来修改Model。

  所谓时势造英雄,那么WPF为MVVM打造了一个什么“时势“呢。 
1. FrameworkElement类中定义了属性DataContext(数据上下文),全部继承于FrameworkElement的类均可以使用这个数据上下文,咱们在XAML中的使用相似Text=”{Binding Path=Name}”的时候,隐藏的含义就是从这个控件的DataContext(即NameObjectViewModel)中取它的Name属性。至关于经过DataContext,使View和Model中存在了一种松耦合的关系。 
2. WPF强大的Binding(绑定)机制,能够在Model发生变化的时候自动更新UI,前提是Model要实现INotifyPropertyChanged接口,在Model数据发生变化的时候,发出ProperyChaned事件,View接收到这个事件后,会自动更新绑定的UI。固然,使用WPF的DenpendencyProperty,发生变化时,View也会更新,并且相对于使用INotifyPropertyChanged,更为高效。 
3. DataTemplate和DataTemplateSelector,即数据模板和数据模板选择器。能够根据Model的类型或者自定义选择逻辑来选择不一样的View。 
4. 使用WPF内置的Command机制,相对来讲,咱们对事件更为熟悉。好比一个Button被点击,一个Click事件会被唤起,咱们能够注册Button的Click事件以处理咱们的逻辑。在这个例子里,我使用的是Command="{Binding Path=ChangeNameCommand}",这里的ChangeNameCommand就是DataContext(即NameObjectViewModel)中的属性,这个属性返回的类型是ICommand。在构建这个Command的时候,设置了CanExecute和Execute的逻辑,那么这个ICommand何时会调用,Button Click的时候会调用么?是的,WPF内置中提供了ICommandSource接口,实现了这个接口的控件就有了触发Command的可能,固然具体的触发逻辑要本身来控制。Button的基类ButtonBase就实现了这个接口,而且在它的虚函数OnClick中触发了这个Command,固然,这个Command已经被咱们绑定到ChangeNameCommand上去了,因此Button被点击的时候咱们构建ChangeNameCommand传入的委托得以被调用。

  正是借助了WPF强大的支持,MVVM自从提出,就得到了好评。那么总结一下,它真正的亮点在哪里呢? 
1. 使代码更加干净,我没使用简洁这个词,由于使用这个模式后,代码量无疑是增长了,但View和Model之间的逻辑更清晰了。MVVM致力打造一种纯净UI的效果,这里的纯净指后台的xaml.cs,若是你编写过WPF的代码,可能会出现事后台xaml.cs代码急剧膨胀的状况。尤为是主window的后台代码,动则上千行的代码,整个window内的控件事件代码以及逻辑代码混在一块儿,看的让人发恶。 
2. 可测试性。更新UI的时候,只要Model更改后发出了propertyChanged事件,绑定的UI就会更新;对于Command,只要咱们点击了Button,Command就会调用,实际上是借助了WPF内置的绑定和Command机制。若是在这层意思上来讲,那么咱们就能够直接编写测试代码,在ViewModel上测试。若是修改数据后获得了propertyChanged事件,且值已经更新,说明逻辑正确;手动去触发Command,模拟用户的操做,查看结果等等。就是把UnitTest也当作一个View,这样Model-ViewModel-View和Model-ViewModel-UnitTest就是等价的。 
3. 使用Attached Behavior解耦事件,对于前面的例子,Button的点击,咱们已经尝试了使用Command而不是传统的Event来修改数据。是的,相对与注册事件并使用,无疑使用Command使咱们的代码更“和谐“,若是能够把控件的所有事件都用Command来提供有多好,固然,控件的Command最多一个,Event却不少,MouseMove、MouseLeave等等,期望控件暴露出那么多Command来提供绑定不太现实。这里提供了一个Attached Behavior模式,目的很简单,就是要注册控件的Event,而后在Event触发时时候调用Command。相似的Sample以下:

public static DependencyProperty PreviewMouseLeftButtonDownCommandProperty = DependencyProperty.RegisterAttached( 
"PreviewMouseLeftButtonDown", 
typeof(ICommand), 
typeof(AttachHelper), 
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(AttachHelper.PreviewMouseLeftButtonDownChanged))); 

public static void SetPreviewMouseLeftButtonDown(DependencyObject target, ICommand value) 
{ 
target.SetValue(AttachHelper.PreviewMouseLeftButtonDownCommandProperty, value); 
} 

public static ICommand GetPreviewMouseLeftButtonDown(DependencyObject target) 
{ 
return (ICommand)target.GetValue(AttachHelper.PreviewMouseLeftButtonDownCommandProperty); 
} 

private static void PreviewMouseLeftButtonDownChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) 
{ 
FrameworkElement element = target as FrameworkElement; 
if (element != null) 
{ 
if ((e.NewValue != null) && (e.OldValue == null)) 
{ 
element.PreviewMouseLeftButtonDown += element_PreviewMouseLeftButtonDown; 
} 
else if ((e.NewValue == null) && (e.OldValue != null)) 
{ 
element.PreviewMouseLeftButtonDown -= element_PreviewMouseLeftButtonDown; 
} 
} 
} 

private static void element_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
{ 
FrameworkElement element = (FrameworkElement)sender; 
ICommand command = (ICommand)element.GetValue(AttachHelper.PreviewMouseLeftButtonDownCommandProperty); 
command.Execute(sender); 
View Code

  这里用到了DependencyProperty.RegisterAttached这个AttachedProperty,关于这个模式,留到下面去讲,这段代码的主要意思就是注册控件的PreviewMouseLeftButtonDown事件,在事件唤起时调用AttachedProperty传入的Command。

  那么是否是这个模式真的就这么完美呢,固然不是,MVVM配上WPF天然是如鱼得水,不过它也有不少不足,或者不适合使用的场合:

1. 这个模式须要Model-ViewModel,在大量数据的时候为每一个Model都生成这样一个ViewModel显然有些过。ViewModel之因此得名,由于它要把Model的属性逐一封装,来给View提供绑定。

2. Command的使用,前面提到过,实现ICommandSource的接口才具有提供Command的能力,那是否是WPF的内置控件都实现了这样的接口呢?答案是否是,不多,只有像Button,MenuItem等少数控件实现了这一接口,像咱们比较经常使用ComboBoxItem就没有实现这一接口。接口没实现,咱们想使用Command的绑定更是无从谈起了。这个时候咱们要使用Command,就不得不本身写一个ComboxBoxCommandItem继承于ComboBoxItem,而后本身实现ICommandSource,而且在Click的时候触发Command的执行了。看起来这个想法不算太好,那不是要本身写不少控件,目的就是为了用Command,也太为了模式而模式了。但像Expression Blend,它就是定义了不少控件,目的就是为了使用Command,提及来也奇怪,本身设计的控件,用起来本身还须要封装,这么多个版本也不添加,这个有点说不过去了。

3. 纯UI,就是在控件后台的cs代码中除了构造函数最多只有一行,this.DataContext = xx; 设置一下数据上下文。固然,我目前的项目代码大都是这样的,仍是那句话,不要为了模式而模式。那么多的控件event,无论是使用Attached模式仍是用一些奇技淫巧用反射来构建出Command,都没什么必要。目前个人作法就是定义一个LoadedCommand,在这个Command中引用界面上的UI元素,ViewModel拿到这个UI元素后在ViewModel中注册控件事件并处理。仍是第一个优势,这么作只是为了让代码更干净,逻辑更清晰,若是都把各个控件事件代码都写在一个xaml.cs中看起来比较混乱。

  谈过了MVVM,接下来重点谈AttachedProperty,这个是很好很强大的feature,也是WPF真正让我有不同感受的地方。前面简单谈过了DependencyProperty的原理,不少初接触WPF的朋友们都会以为DP很绕,主要是被它的名字和咱们的第一直觉所欺骗。若是咱们定义了一个DP,MyNameProperty,类型是string的。那么在DependencyObject上,我谈过了有个百宝囊,就是EffectiveValueEntry数组,它内部最终储存MyName的值也是string,这个DependencyProperty(即MyNameProperty)是个静态属性,是在你设置读取这个string的时候起做用的,如何起做用是经过它注册时定义的propertyMetadata决定的。

  简单来讲就是DependencyObject能够使用DependencyProperty,但二者没有从属关系,你在一个DependencyObject中定义了一个DP,在另外一个DependencyObject也能够使用这个DP,你在另外一个DependencyObject中写一个CLR属性使用GetValue和SetValue封装这个DP是同样的。惟一DependencyProperty和DependencyObject有关联的地方就是你注册的时候,DP保存在全局静态DP的Hashtable里的键值是经过注册时的名字和这个DependencyObject的类型的hashcode取异或生成的。但这个键值也能够不惟一,DP提供了一个AddOwner方法,你能够为这个DP在全局静态DP中提供一个新键值,固然,这两个键值指向同一个DP。

  既然如此,那么为何有DependencyProperty.Register和DependencyProperty.RegisterAttached两种方法注册DP呢。既然DP只是一个引子,经过GetValue和SetValue,传入DependencyObject就能够取得存储在其中EffectiveValueEntry里面的值,这两个不是同样的么?恩,原理上是一个,区别在于,前面提到,一个DependencyProperty里面会有多个propertyMetadata,好比说Button定义了一个DP,咱们又写了一个CustomButton,继承于Button。咱们在CustomButton的静态函数中调用了前面DP的OverrideMetadata函数,DP的OverrideMetadata会涉及到Merge操做,它要把新旧的propertyMetadata合二为一成一个,做为新的propertyMetadata,而这个overrideMetadata过程须要调用时传入的类型必须是DependencyObject的。DependencyProperty.Register和DependencyProperty.RegisterAttached的区别是前者内部调用了OverrideMetadata然后者没有,也就意味着Rigister方法只能是DependencyObject调用,然后者能够在任何对象中注册。

  就这一个区别么?恩,还有的,默认的封装方法,Register是使用CLR属性来封装的,RegisterAttached是用静态的Get,Set来封装的。Designer反射的时候,遇到静态的封装会智能感知成相似Grid.Column=“2”这样的方式。这个就相似于非要说菜刀有两大功能,一是砍菜,二是砍人。你要感到纳闷,不是由于菜刀有刀刃么?它会和你解释,不一样不一样,砍菜进行了优化,你能够用手握着,砍人犯法,最好飞出去…

  那么为何微软要把这个注册过程分为Register和RegisterAttached两类呢?就是为了强调Attach这个概念,这个过程就是DependencyObject(至关于银行金库,有不少箱子)经过DependencyProperty(至关于开箱子的钥匙)取得本身箱子里的财宝同样,固然这些全部的钥匙有人统一管理(全局的HashTable),你来拿钥匙的时候还要刁难一下你(经过钥匙上的附带的propertyMetadata)检查一下你的身份啦,你存取东西要发出一些通知啦等等。这个Attach,翻译过来叫附加,所谓的AttachedProperty(附加属性),就是说人家能够随时新配一把钥匙来你这新开一个箱子,或者拿一把旧钥匙来你这新开个箱子,谁让你箱子多呢?

  强调了这么多,只是为了说明一点,这个Attach的能力不是由于你注册了RegisterAttached才具有的,而是DependencyProperty自己设计就支持的。那么这个设计能为咱们开发程序带来哪些好处呢?

  从继承和接口实现来讲,人们初期阶段有些乱用继承,后来出现了接口,只有明确有IS-A语义的才用继承,能力方面的用接口来支持。好比飞行,那么通常会定义到一个IFlyable的接口,咱们实现这个接口以得到飞行的能力。那么这个能力的得到要在类的设计阶段继承接口来得到,那么做为一个已经成熟的人,我是大雄,我要飞,怎么办?

AttachedProperty来救你。代码以下:

public partial class Window1 : Window 
{ 
public Window1() 
{ 
InitializeComponent(); 
this.DataContext = new DragonFlyViewModel(); 
} 
}

public interface IFlyHandler 
{ 
void Fly(); 
}

public class DragonFlyViewModel : IFlyHandler 
{ 
public void Fly() 
{ 
MessageBox.Show("送你个竹蜻蜓,飞吧!"); 
} 
}


public class FlyHelper 
{ 
public static readonly DependencyProperty FlyHandlerProperty = 
DependencyProperty.RegisterAttached("FlyHandler", typeof(IFlyHandler), typeof(FlyHelper), 
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnFlyHandlerPropertyChanged))); 

public static IFlyHandler GetFlyHandler(DependencyObject d) 
{ 
return (IFlyHandler)d.GetValue(FlyHandlerProperty); 
} 

public static void SetFlyHandler(DependencyObject d, IFlyHandler value) 
{ 
d.SetValue(FlyHandlerProperty, value); 
} 

public static void OnFlyHandlerPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) 
{ 
FrameworkElement element = target as FrameworkElement; 
if (element != null) 
{ 
IFlyHandler flyHander = e.NewValue as IFlyHandler; 
element.MouseLeftButtonDown += new MouseButtonEventHandler((sender, ex) => 
{ 
if (flyHander != null) 
{ 
flyHander.Fly(); 
} 
}); 
} 
} 
} 

<Window x:Class="WpfApplication7.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:local="clr-namespace:WpfApplication7" 
Title="Window1" Height="300" Width="300"> 
<Grid> 
<Label Margin="72,58,88,113" Name="label1" Background="Yellow" 
local:FlyHelper.FlyHandler="{Binding}">我叫大雄我不会飞</Label> 
</Grid> 
</Window
View Code

  这是一个最简单的模式应用,固然,还不是很完美,不过已经能够起飞了。咱们在FlyHelper中使用DependencyProperty.RegisterAttached注册了一个AttachedProperty,在OnFlyHandlerPropertyChanged中订阅了element的MouseLeftButtonDown事件,事件处理就是”起飞”。这里定义了一个IFlyHandler的接口,使用ViewModel模式,ViewModel去实现这个接口,而后使用local:FlyHelper.FlyHandler="{Binding}"绑定,这里{Binding}没有写path,默认绑定到DataContext自己,也就是DragonFlyViewModel上。

  你说什么?你要去追小静?那必定要帮你。你往脑门上点一下,看,是否是会飞了?怎么样,戴着竹蜻蜓的感受很好吧,^_^。大雄欣喜若狂,连声感谢。不过,这么欺骗一个老实人的感受不太好,实话实说了吧。你真是有宝物不会用啊,你胸前挂着那是啥?小口袋?百宝囊?那是机器猫的口袋,汽车大炮时空飞船,什么掏不出来啊。哦,你嫌竹蜻蜓太慢了?你等等。

public partial class Window1 : Window 
{ 
public Window1() 
{ 
InitializeComponent(); 
this.DataContext = new DragonFlyViewModel(); 
} 

private void button1_Click(object sender, RoutedEventArgs e) 
{ 
this.DataContext = new FighterViewModel(); 
} 
} 



public interface IFlyHandler 
{ 
void Fly(); 
} 

public class DragonFlyViewModel : IFlyHandler 
{ 
public void Fly() 
{ 
MessageBox.Show("送你个竹蜻蜓,飞吧!"); 
} 
} 

public class FighterViewModel : IFlyHandler 
{ 
public void Fly() 
{ 
MessageBox.Show("送你驾战斗机,为了小静,冲吧!"); 
} 
} 



public class FlyHelper 
{ 
private IFlyHandler _flyHandler; 

public FlyHelper(IFlyHandler handler, FrameworkElement element) 
{ 
_flyHandler = handler; 
element.MouseLeftButtonDown += new MouseButtonEventHandler(element_MouseLeftButtonDown); 
} 

void element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
{ 
if (_flyHandler != null) 
{ 
_flyHandler.Fly(); 
} 
} 

private void UpdateFlyHandler(IFlyHandler handler) 
{ 
_flyHandler = handler; 
} 

#region FlyHelper 
public static readonly DependencyProperty FlyHelperProperty = 
DependencyProperty.RegisterAttached("FlyHelper", typeof(FlyHelper), typeof(FlyHelper), 
new FrameworkPropertyMetadata(null)); 

public static FlyHelper GetFlyHelper(DependencyObject d) 
{ 
return (FlyHelper)d.GetValue(FlyHelperProperty); 
} 

public static void SetFlyHelper(DependencyObject d, FlyHelper value) 
{ 
d.SetValue(FlyHelperProperty, value); 
} 
#endregion 

#region FlyHandler 
public static readonly DependencyProperty FlyHandlerProperty = 
DependencyProperty.RegisterAttached("FlyHandler", typeof(IFlyHandler), typeof(FlyHelper), 
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnFlyHandlerPropertyChanged))); 

public static IFlyHandler GetFlyHandler(DependencyObject d) 
{ 
return (IFlyHandler)d.GetValue(FlyHandlerProperty); 
} 

public static void SetFlyHandler(DependencyObject d, IFlyHandler value) 
{ 
d.SetValue(FlyHandlerProperty, value); 
} 

public static void OnFlyHandlerPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) 
{ 
FrameworkElement element = target as FrameworkElement; 
if (element != null) 
{ 
FlyHelper helper = (FlyHelper)element.GetValue(FlyHelperProperty); 
if (helper == null) 
{ 
IFlyHandler handler = e.NewValue as IFlyHandler; 
if (handler != null) 
{ 
helper = new FlyHelper(handler, element); 
element.SetValue(FlyHelperProperty, helper); 
} 
} 
else 
{ 
IFlyHandler handler2 = e.NewValue as IFlyHandler; 
//handler2 may be null, this usually happened when this.DataContext = null, release IFlyHandler. 
helper.UpdateFlyHandler(handler2); 
} 
} 
} 
#endregion 
} 
View Code

  这里就是一个完整的Attached模式,这里我添加了一个新的AttachedProperty,类型是FlyHelper,当local:FlyHelper.FlyHandler="{Binding}"绑定值发生变化时,判断传入的这个DependencyObject内是否有FlyHelper对象,没有,构造一个,而后塞入到这个DependencyObject中去;若是有,则更新FlyHelper内持有的IFlyHandler对象。这个Attached模式的好处在于,这个辅助的Helper对象是在运行时构造的,构造以后塞入到UI对象(DependencyObject)中去,仅是UI对象持有这个引用,UI对象被释放后这个Helper对象也被释放。FlyHelper对象用于控制什么时候”起飞”,至于怎么飞则依赖于IFlyHandler这个接口,这层依赖是在绑定时注入的,而这个绑定最终是运用了DataContext这个数据上下文,和MVVM模式搭配的很完美。这也就是MVVM模式中强调的,也就是惟一的依赖,设置控件的DataContext。

  回顾一下,做于例子中的Label,是不具有“飞行“能力的。这种不具有具体说就是不知道何时触发动做,也不知道触发了以后该干什么。经过一个Attach模式使它具有了这个能力,并且能够随时更新动做。简直达到了一种让你飞,你就飞的境界,值得为它喝彩。

  鉴于这种动态添加控件的能力,这种模式也被称为Attached Behavior。在Blend 3中,也加入了Behaviors的支持,不少通用的能力,均可以用Behavior来把它抽出来,好比缩放,DragDrop等等。我没有具体研究过Blend的Behavior,应该也是这种方法或演变吧。在实际项目中,我也大量使用了MVVM和Attached Behavior,配上CommandBinding,Unit Test,脚本化UIAutomation,以及Prism等框架,对一些比较大型的项目,仍是颇有帮助的。

  顺着DP这条线讲下来,仍是蛮有味道的。固然,WPF中还有一些比较新的概念,包括逻辑树和视觉树,路由事件,Style和Template等等。其实纵看WPF,仍是有几条主线的,包括刚才讲到的DP,Threading Model与Dispatcher,视觉树和依赖它产生的路由,Template和Style等等。那么就回到开头了,如何学好WPF呢?

  其实写这篇文章以前,我是常常带着这疑问的。如今新技术推出的很快,虽然说没什么技术是凭空产生,都是逐渐衍变而来的。但是真学下去也要花成本,那怎么样才是学好了呢,怎么能融入到项目呢?后来总结了下,我须要了解这么一些状况: 
  1. 这门技术是否成熟,前景如何? 
  2. 摆脱宣传和炒做,这门技术的优缺点在哪里? 
  3. 但愿看到一些对这门技术有总体把握的文章,能够不讲细节,主要是帮我理出一个轮廓,最好和个人知识树连一连。 
  4. 有没有应用的成功案例。 
 5. 最重要的,呵呵,有没有能够下载的电子书。

  关于WPF,如今讲解的书籍和资料已经蛮多了。随着.NET Framework的升级,包括性能以及辅助工具的支持也愈来愈好了。但不得不说,WPF学习的时间成本仍是很大的。WPF的设计很重,带着很浓的设计痕迹,查看WPF的源码,也许你会有种很熟悉的感受。这种熟悉不是那种流畅美妙之感,到有种到了项目后期,拿着性能测试去优化,拿着Bug报告乱堵窟窿的感受。

文本滑动功能实现

      
  #region 构造器
        public TextView()
        {
            InitializeComponent();
            txtFont = (this.Content as Canvas).Children[0] as TextBlock;
            Binding binding = new Binding();
            binding.Source = this.DataContext;
            binding.Path = new PropertyPath("CurrentElement.Src");
            BindingOperations.SetBinding(this, TextProperty, binding);
        }
        #endregion
 
        #region 属性
        public string Src
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
 
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Src", typeof(string), typeof(TextView), new UIPropertyMetadata(null, CurrentSrcChanged));
 
        private static void CurrentSrcChanged(object element, DependencyPropertyChangedEventArgs e)
        {
            TextView textView = (TextView)element;
            Canvas canvas = (Canvas)textView.Content;
            TextBlock textBlock = (TextBlock)canvas.Children[0];
            XElement xText = XElement.Load(textView.Src);
            textBlock.Text = xText.Attribute("content").Value.ToString();
            canvas.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString(xText.Attribute("bgcolor").Value.ToString()));
            textBlock.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(xText.Attribute("fgcolor").Value.ToString()));
            textBlock.FontFamily = (FontFamily)(new FontFamilyConverter().ConvertFromString(xText.Attribute("font").Value.ToString()));
            textBlock.FontSize = Convert.ToInt32(xText.Attribute("size").Value.ToString());
            textBlock.TextWrapping = TextWrapping.Wrap;
        }
        #endregion
 
        #region 定时器实现
        //private void UserControl_Loaded(object sender, RoutedEventArgs e)
        //{
        // System.Threading.Thread thread = new System.Threading.Thread(Scroll);
        // timer.Interval = 50;
        // timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
        // timer.Start();
        // thread.Start();
        //}
 
        //void timer_Elapsed(object sender, ElapsedEventArgs e)
        //{
        // offset++;
        // Scroll();
        //}
 
        //private void Scroll()
        //{
        // Action action;
        // action = ()=>scrollViewer.ScrollToVerticalOffset(offset);
        // Dispatcher.BeginInvoke(action);
        //}
 
        //Timer timer = new System.Timers.Timer();
        //private double offset = 0;
 
        #endregion
 
        #region 动画实现
        private void CeaterAnimation()
        {
            if (txtFont == null || txtFont.ActualHeight < (this.Content as Canvas).ActualHeight)
            {
                return;
            }
            //建立动画资源
            Storyboard storyboard = new Storyboard();
 
            //移动动画
            DoubleAnimationUsingKeyFrames HeightMove = new DoubleAnimationUsingKeyFrames();
            Storyboard.SetTarget(HeightMove, txtFont);
            DependencyProperty[] propertyChain = new DependencyProperty[]
            {
                TextBlock.RenderTransformProperty,
                TransformGroup.ChildrenProperty,
                TranslateTransform.YProperty,
            };
            Storyboard.SetTargetProperty(HeightMove, new PropertyPath("(0).(1)[3].(2)", propertyChain));
            HeightMove.KeyFrames.Add(new EasingDoubleKeyFrame(0, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 2))));
            HeightMove.KeyFrames.Add(new EasingDoubleKeyFrame(-txtFont.ActualHeight, KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, (int)(txtFont.ActualHeight / 70)))));
            storyboard.Children.Add(HeightMove);
            storyboard.RepeatBehavior = RepeatBehavior.Forever;
            storyboard.Begin();
 
        }
 
        private void TextBlock_Loaded(object sender, RoutedEventArgs e)
        {
            CeaterAnimation();
        }
 
        TextBlock txtFont;
        #endregion
View Code

 

list和observableCollection的区别

list删除以后不会通知binding的改变,observableCollection则会通知,比list要厉害一点。

wpf数据绑定为何只能帮顶属性而不是字段(摘抄)

C#属性是域的扩展(即一般说的成员变量或字段等)它配合C#中的域(字段)使用,使之构造一个安全的应用程序,为何说经过属性来替代域会提升应用程序的安全呢?

    缘由就在于C#属性经过访问器(Accessors)用进行数据访问.因此C#的属性能够设置为只读或只写. 而字段却没有这样的功能(只可设置只读).咱们都知道在程序中有时咱们是不容许用户修改一些属性的,好比地球是圆的。原则上咱们是不能修改此属性.那么咱们就能够经过一个属性来实现这样的功能.让它设置为只读属性.

    属性的特色:C#属性是对类中的字段(fields)的保护,像访问字段同样来访问属性。同时也就封装了类的内部数据。每当赋值运算的时候自动调用set访问器,其余时候则调用get访问器。 以 帕斯卡命名 不能冠以Get/Set。静态属性是经过类名调用的!

    前面咱们说到属性是字段的扩展,咱们都知道若是要在类外部访问字段,就要公开(Public)这个成员字段。可是若是咱们真的这样作了,那这个成员字段的就能够被任意访问(包括修改,读取).那怎么办呢? 用一个属性就能够解决这个问题.

C#属性是经过Get(读取)、Set(设置)来访问属性的. 

public class Test 
{ 
    public Test() 
       { 
              // 
// TODO: 在此处添加构造函数逻辑 
// 
        } 
    //为了说明问题这里咱们用中文 
    public string 地球的形状; 
} 
在上面的例子里"地球的形状"这个字段就能够任意的访问,不受任何的束缚.可是咱们都知道地球是圆的,是不容许修改的一个特性,那怎么办呢?用一个属性就能够轻松的解决这个问题. 
public class Test 
{ 
    public Test() 
       {         
              // 
// TODO: 在此处添加构造函数逻辑 
// 
       } 
    //为了说明问题这里咱们用中文 
    private string 地球的形状="";//私有的成员变量,它对于外部是不可见的. 
    public string 地球形状 
        { 
           get 
           { 
              return 地球的形状;//这里将私有成员变量地球的形状返回给"地球的形状" 
           } 
        } 
} 
这里咱们只能够读取属性"地球形状",而不能够写,若是强制写编译器就会提示出错.这样咱们就能够经过类来访问属性. 
Test MyTt=new Test();//实例化类 
string MyTemp=MyTt.地球形状;//读取类的属性 
下面咱们说一下写的应用. 
public class Test 
{ 
    public Test() 
       { 
              // 
// TODO: 在此处添加构造函数逻辑 
// 
        } 
    //为了说明问题这里咱们用中文 
    private string 你的名字;//私有的成员变量,它对于外部是不可见的. 
    public string 名字 
        { 
           get 
           { 
              return 你的名字;//这里将私有成员变量"你的名字"的形状返回给"名字" 
           } 
           set 
           { 
               你的名字=value;//这里的value将等于"名字" 这个属性值
           } 
        } 
} 
View Code

 

这样咱们就能够对属性读取和写了. 

Test MyTt=new Test();//实例化类 
MyTt.名字="Simon"//设置属性 
String MyTemp=MyTt.名字;读取属性值 
经过上面的例子咱们能够看出属性只不过是作了一个中介的角色而已,真正工做的仍是字段(域),但这样作能够更面向对象,写出更安全的应用程序。

     C#提供了一个处理此概念的更清爽的方式。在C#中,get和set方法是内在的,而在Java和C++里则需人为维护。C#的处理方式有诸多优势。它鼓励程序员按照属性的方式去思考—把这个属性标为可读写的和只读的哪一个更天然?或者根本不该该为属性?若是你想改变你的属性的名称,你只要检查一处就能够了。

    C#中属性这种机制使得在保证封装性的基础上实现了访问私有成员的便捷性。一个支持属性的语言将有助于得到更好的抽象。

    来自MSDN中的内容:

属性和属性过程

    能够使用属性和字段在对象中存储信息。属性使用属性过程控制如何设置或返回值,而字段只是公共变量。属性过程是在属性定义中声明的代码块,可用于在设置或检索属性值时执行代码。

    具备两种类型的属性过程:用于检索属性值的 Get 属性过程和用于为属性分配值的 Set 属性过程。例如,存储银行账户余额的属性可能会在 Get 属性过程当中使用代码以在返回可用余额以前记入利息并检查服务费。而后,您能够使用 Set 属性过程验证余额并防止它以不正确的方式更新。简而言之,属性过程容许对象保护和验证本身的数据。

只读和只写属性
    大多数属性都具备 Get 和 Set 属性过程,这两个属性过程可用于读取和修改存储在内部的值。然而,您能够使用 ReadOnly 或 WriteOnly 修饰符来限制对属性进行修改或读取。

    只读属性不能具备 Set 属性过程。这种属性用于须要公开但不容许修改的项。例如,能够使用只读属性来提供计算机处理器的速度。
    只写属性不能具备 Get 属性过程,它们用于使用不该或不能存储在对象中的数据配置对象。例如,只写属性可用于获取密码并在不存储该密码的状况下更改对象的状态。

wpf的command传递参数给viewmodel

path是指定名字控件的一个依赖属性。传递的是一个字符串,若是是一个整形的话,还须要将其转换成为string。
同时须要这个dll,
而且引用以下命名空间。
使用方式以下:
<Controls:DataPager x:Name="dataPager" PageSize="25" Grid.Row="1" TotalCount="{Binding Path=SchedualTotalModel.Total}">
            <I:Interaction.Triggers>
                <I:EventTrigger EventName="PageChanged">
                    <I:InvokeCommandAction Command="{Binding PageChangeCommand}" CommandParameter="{Binding ElementName=dataPager,Path=PageIndex,Converter={StaticResource userConverter}}" />
                </I:EventTrigger>
                <I:EventTrigger EventName="Loaded">
                    <I:InvokeCommandAction Command="{Binding PageSizeCommand}" CommandParameter="{Binding ElementName=dataPager,Path=PageSize,Converter={StaticResource userConverter}}"/>
                </I:EventTrigger>
            </I:Interaction.Triggers>
        </Controls:DataPager>
View Code

 

wpf 事件聚合器

首先要有个事件类:
public class QueueSearchConEvent : CompositePresentationEvent<SearchCon>
    {
 
    }
 
    public class SearchCon
    {
       public string _partyId;
       public string _consultionId;
       public string _doctorId;
       public string _stationSelectedIndex;
       public string _triageSelectedIndex;
       public string _patientName;
       public SearchCon(string partyId, string consultionId, string doctorId, string stationSelectedIndex, string triageSelectedIndex, string patientName)
        {
            this._partyId = partyId;
            this._consultionId = consultionId;
            this._doctorId = doctorId;
            this._stationSelectedIndex = stationSelectedIndex;
            this._triageSelectedIndex = triageSelectedIndex;
            this._patientName = patientName;
        }
   
    }
 
View Code

其次,在构造器之中注册一下事件:

#region 构造器
        public QueueListViewModel()
        {
            if (!IsInDesignMode)
            {
                _commandParameters = this.UnityContainer.Resolve<CommandParameters>();
                TrigeService = this.UnityContainer.Resolve<ITrigeService>();
                _regionManager = this.UnityContainer.Resolve<IRegionManager>();
                _container = this.UnityContainer.Resolve<IUnityContainer>();
                PageChange(1, 2);
                this.EventAggregator.GetEvent<QueueSearchConEvent>().Subscribe(ScreeningResults);
            }
        }
        #endregion
View Code

最后,发布事件,也就是用事件(触发某个方法,来引用。能够在项目的任何模块):

private void SelectButtonChanged(string _partyId, string _consultionId, string _doctorId)
        {
 
            this.EventAggregator.GetEvent<QueueSearchConEvent>().Publish(new SearchCon(_partyId, _consultionId, _doctorId, _stationSelectedIndex, _triageSelectedIndex, _patientName));
        }
View Code

 

wpf中*和auto的区别,以及二者和滚动条的区别

Auto 表示自动适应显示内容的宽度, 如自动适应文本的宽度,文本有多长,控件就显示多长.

* 则表示按比例来分配宽度.

 

<ColumnDefinition Width="3*" />
<ColumnDefinition Width="7*" />

一样,行能够这样定义

<RowDefinition Height="3*" />
<RowDefinition Height="7*" />

这些数字能够是小数.
若是数字缺省,则默认是1.
在这个例子中, 列2的宽度是列1的1.5倍.

<ColumnDefinition Width="1.5*" />
<ColumnDefinition />

Auto和*能够混合使用. 在这个例子中,后两行的宽度在前两行分配完以后,按比例获取剩余的宽度.

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto" />  <!-- Auto-fit to content, 'Hi' -->
    <ColumnDefinition Width="50.5" />  <!-- Fixed width: 50.5 device units) -->
    <ColumnDefinition Width="69*" />   <!-- Take 69% of remainder -->
    <ColumnDefinition Width="31*"/>    <!-- Take 31% of remainder -->
</Grid.ColumnDefinitions>
<TextBlock Text="Hi" Grid.Column="0" />

 

auto以后,若是缩小控件显现不出滚动条,而*则会显现出来滚动条。

用WPF实现查找结果高亮显示(摘抄)

概述

咱们常常会遇到这样的需求:到数据库里查找一些关键字,把带这些关键字的记录返回显示在客户端上。但若是仅仅是单纯地把文本显示出来,那很不直观,用户不能很轻易地看到他们想找的内容,因此一般咱们还要作到“高亮显示”。

若是是用BS架构去实现,应该很简单,把相应的关键字加上一些label,而后给label定样式便可,或者直接用js在客户端渲染,减小服务器的负担,但CS架构就相对麻烦一点,我这里用WPF写了一个demo,实现了这个功能的演示:

另外本demo还包括了一些很是有用的wpf的小技巧。

功能介绍

因为这只是一个简单的DEMO,我和以往的风格同样,把它作成了“零配置”,我用一个csv文件和LINQ to Object来取代DBMS,执行一些简单的查询操做。

查询方式分为两种,一种是Full Match,表示全字符匹配,另外一种是Any Key,表示用空格断开查询字符串,逐个关键字查询。

这个程序的显示区域使用了ListView控件,之因此使用ListView而不是DataGrid,主要是ListView能很轻易地自适应行高,而DataGrid的行高是固定的,但若是你要换DataGrid去作的话,应该也是同一个道理。

高亮显示功能分析与实现

要实现高亮显示,咱们能够这么作:在界面上放置一个TextBlock,叫tbTest,而后执行下面的代码:

tbTest.Inlines.Clear();
tbTest.Inlines.Add( new Run("The"){ Background = Brushes.Yellow });
tbTest.Inlines.Add( " quick brown fox jumps over ");
tbTest.Inlines.Add( new Run("the") { Background = Brushes.Yellow });
tbTest.Inlines.Add( new Run(" lazy dog."));
View Code

就能看到这样的“高亮”效果:

遗憾的是Inlines这个属性并不是“依赖属性”(Dependency Property),你不能轻易把一个字符串或对象“绑定”给它。个人作法是建立一个用户控件,其中只包含一个TextBlock:

<UserControl x:class="HighlightDispDemo.HighlightTextBlock"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <TextBlock Name="innerTextBlock" TextWrapping="Wrap">
    </TextBlock>
</UserControl>
View Code

再给它增长一个叫“HlContent”的依赖属性,其类型为自定义的HighlightContent:

        public static readonly DependencyProperty HighlightContentProperty = DependencyProperty .Register( "HlContent", 
      typeof(HighlightContent), 
      typeof( HighlightTextBlock), 
      new FrameworkPropertyMetadata( null, OnHtContentChanged));

        [ Description("获取或设置高亮显示的内容")]
        [ Category("Common Properties")]
        public HighlightContent HlContent
        {
            get { return(HighlightContent)GetValue( HighlightContentProperty); }
            set { SetValue( HighlightContentProperty, value); }
        }
View Code

HighlightContent的定义以下:

    public enum HighlightContentMode
    {
        FullMatch,
        AnyKey
    };

    public class HighlightContent
    {
        public string Content { get; set; }
        public static string ToHighlight { get; set; }
        public static HighlightContentMode Mode { get; set; }
    }
View Code

 

其中ToHighlight属性表示要高亮显示的“键”,而Mode属性则用来指明用“Full Match”仍是“Any Key”模式,考虑到同一时间只有一种高亮显示,我把这两个属性定义为static。

“HlContent”的内容变动通知回调函数:

        private static void OnHtContentChanged(DependencyObject sender, DependencyPropertyChangedEventArgs arg)
        {
            if(sender is HighlightTextBlock)
            {
                HighlightTextBlock ctrl = sender as HighlightTextBlock ;
                HighlightContent content = ctrl.HlContent ;
                ctrl.innerTextBlock.Inlines.Clear();
                if(content != null)
                {
                    ctrl.innerTextBlock.Inlines.AddRange(MakeRunsFromContent( content));
                }
            }
        }

        private static IEnumerable<Run> MakeRunsFromContent(HighlightContent content)
        {
             //此函数功能是:将要显示的字符串根据key及mode,拆分红不一样的Run片断
             //代码较多,从略
        }
View Code

这样一来,咱们就能够用自定义的HighlightTextBlock来取代Textblock实现绑定了。

绑定到ListView(摘抄)

ListView的默认的Column是确定不支持“高亮”显示的了,如今咱们来自定义Template:

        <ListView ItemContainerStyle="{DynamicResource CustomListViewItemStyle}" Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Name="lvContent" AlternationCount="2">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="OS Name" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <hld:HighlightTextBlock HlContent="{Binding Path=OsName,Converter={StaticResource converterHlContent}}"></hld:HighlightTextBlock>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="File System" Width="150">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <hld:HighlightTextBlock HlContent="{Binding Path=FileSystem,Converter={StaticResource converterHlContent}}"></hld:HighlightTextBlock>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Desktop" Width="200">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <hld:HighlightTextBlock HlContent="{Binding Path=Desktop,Converter={StaticResource converterHlContent}}"></hld:HighlightTextBlock>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
View Code

能够看到,Template中使用了前面咱们自定义的HighlightTextBlock控件,它们绑定的Path分别是OsName,FileSystem和Desktop,其实这都是string,而HlContent须要的是HighlightContent类型,因此咱们还得指定一个转换器,转换器代码以下:

    [ValueConversion(typeof(string), typeof(HighlightContent))]
    public class HlContentConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return new HighlightContent {Content = (string)value};
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
View Code

 

使用CsvHelper来读取CSV文件(摘抄)

此次我没有使用DBMS,其实DEMO项目能不用DBMS就不用了,不然部署困难,不利于问题分析。CsvHelper能够从github上获取,地址是:https://github.com/JoshClose/CsvHelper

它的帮助写得稍微有点潦草(我的感受),我这里稍稍补充说明下:CsvHelper的思路就是把csv文件转为一个可枚举的集合,其中的一行转为集合中的一个对象,那么一列就对应到这个对象的一个属性,那么究竟哪一列转为那个属性呢?咱们得告诉它,这就是“Map”,了解了这个以后看一下下面的代码,一切都清楚了。

    public class LinuxInfo
    {
        public string OsName { get; set; }
        public string FileSystem { get; set; }
        public string Desktop { get; set; }
    }

    public class LinuxMap : CsvClassMap<LinuxInfo>
    {
        public override void CreateMap()
        {
            Map(m => m.OsName).Index(0);
            Map(m => m.FileSystem).Index(1);
            Map(m => m.Desktop).Index(2);
        }
    }
View Code

上面代码是对象及Map定义。下面是执行读取和转换的操做。

TextReader tr = new StreamReader("linux.csv", Encoding.UTF8);
     CsvReader csv = new CsvReader(tr);
     csv.Configuration.RegisterClassMap<LinuxMap>();
     csv.Configuration.HasHeaderRecord = false; //表示csv文件不带header行
     _listData = csv.GetRecords<LinuxInfo>().ToList();
View Code

 

ListView的隔行背景样式(摘抄)

把ListView的AlternationCount属性设为2,并指定ItemContainerStyle="{DynamicResource CustomListViewItemStyle}"。Style这样定义:

     <Style x:Key="CustomListViewItemStyle" TargetType="{x:Type ListViewItem}">
          <Style.Triggers>
               <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                    <Setter Property="Background" Value="#DDEEFF"></Setter>
               </Trigger>
          </Style.Triggers>
     </Style>
View Code

 

让TextBox得到焦点时全选文本(摘抄)

这个功能得在App.xml.cs中作一些全局处理:

 
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            //为了让TextBox可以在得到焦点的时候自动选中其中文本,特地添加此全局事件处理
            EventManager.RegisterClassHandler(typeof(TextBox), UIElement.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(SelectivelyHandleMouseButton), true);
            EventManager.RegisterClassHandler(typeof(TextBox), UIElement.GotKeyboardFocusEvent, new RoutedEventHandler(SelectAllText), true);
        }

        private static void SelectivelyHandleMouseButton(object sender, MouseButtonEventArgs e)
        {
            var textbox = (sender as TextBox);
            if (textbox != null && !textbox.IsKeyboardFocusWithin)
            {
                if (e.OriginalSource.GetType().Name == "TextBoxView")
                {
                    e.Handled = true;
                    textbox.Focus();
                }
            }
        }

        private static void SelectAllText(object sender, RoutedEventArgs e)
        {
            var textBox = e.OriginalSource as TextBox;
            if (textBox != null)
                textBox.SelectAll();
        }
View Code

完整代码下载

HighlightDispDemo.7z(Visual Studio 2010)

使用WPF中GridViewColumn的CellTemplateSelector(摘抄)

 GridViewColumn中的CellTemplateSelector使用起来有一点须要注意:DataTemplateSelector(数据模板选择器)的操做对象是ListView的直接绑定对象,而不是对象在该列的属性。事实上若是设置了DisplayMemberBinding,CellTemplateSelector和CellTemplate都是无效的。

  这个问题从一个简单的示例上能够看出来,这样一个简单的对象:

    
public class Item

    {

        public int Int { get; set; }

        public bool Bool { get; set; }

    }

 

  用ListView绑定一系列的Item对象,而后分两列显示这个Int和Bool。Bool那列确定显示True或者False(由于没定义CellTemplateSelector或者CellTemplate的话,WPF默认显示对象的ToString)。如今用一个DataTemplateSelector,来根据Bool是True或者False,显示一个红框或者绿框。

  此时DataTemplateSelector你有可能这样定义(错误的):

class BoolSelector : DataTemplateSelector

    {

        public DataTemplate TrueTemplate { get; set; }

        public DataTemplate FalseTemplate { get; set; }

 

        public override DataTemplate SelectTemplate(object item, DependencyObject container)

        {

            //错误代码

            if ((bool)item)

                return TrueTemplate;

            return FalseTemplate;

        }

    }

 

  而后在XAML中,定义好这个DataTemplateSelector,设置DisplayMemberBinding和CellTemplateSelector:

    
<Window.Resources>

        <loc:BoolSelector x:Key="sel">

            <loc:BoolSelector.TrueTemplate>

                <DataTemplate>

                    <Rectangle Fill="Green" Width="20" Height="20"/>

                </DataTemplate>

            </loc:BoolSelector.TrueTemplate>

            <loc:BoolSelector.FalseTemplate>

                <DataTemplate>

                    <Rectangle Fill="Red" Width="20" Height="20"/>

                </DataTemplate>

            </loc:BoolSelector.FalseTemplate>

        </loc:BoolSelector>

    </Window.Resources>

    <ListView Name="list">

        <ListView.View>

            <GridView>

                <GridView.Columns>

                    <GridViewColumn Header="int" DisplayMemberBinding="{Binding Int}"/>

                    <GridViewColumn Header="bool" DisplayMemberBinding="{Binding Bool}" CellTemplateSelector="{StaticResource sel}" />

                </GridView.Columns>

            </GridView>

        </ListView.View>

    </ListView>

 

  最后ListView仍然只输出True和False。

  缘由是若是设置了GridViewColumn的DisplayMemberBinding,CellTemplateSelector和CellTemplate都是无效的。一样CellTemplateSelector的对应DataTemplateSelector的针对对象是数据绑定对象(本例中的Item类),而不是对象的属性(本例中的Bool值)。

  因此首先修改上面的DataTemplateSelector的SelectTemplate,让其针对Item对象:

        
public override DataTemplate SelectTemplate(object item, DependencyObject container)

        {

            if (((Item)item).Bool)

                return TrueTemplate;

            return FalseTemplate;

        }

  接着不使用GridViewColumn的DisplayMemberBinding。直接用CellTemplateSelector:

<GridViewColumn Header="bool" CellTemplateSelector="{StaticResource sel}" />

  结果才会显示正确:

  

 

  本文来自刘圆圆的博客,原文地址:http://www.cnblogs.com/mgen/archive/2011/11/24/2262465.html

wpf窗体样式

重写窗体样式,如今我接触的,主要是下面这个方法,它有以下几个步骤:

1、写一个继承自window的ResourceDictionary,而且要设置以下的style
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="True" />
 
2、grid布局窗体,添加最大化最小化关闭等按钮。而且在继承window的类中实现这些按钮的功能,这些功能也继承自window自带的最大最小化和关闭。
    如何让此类能访问ResourceDictionary中的按钮,只须要将其做为app.xaml中的资源文件引用。
      引用以下:
 <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resource Dictionaries/MacStyledWindow.xaml" />
                <ResourceDictionary Source="Resources/TabItemResource.xaml"/>
                <ResourceDictionary Source="Resources/WindowRegionResource.xaml"/>
                <ResourceDictionary Source="Resources/Styles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
    调用方法以下:
            ControlTemplate baseWindowTemplate = (ControlTemplate)App.Current.Resources["MacWindowTemplate"];
            //set up background image
            Button skinBtn = (Button)baseWindowTemplate.FindName("SkinBtn", this);
            skinBtn.Click += delegate
            {
                Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
                if (dlg.ShowDialog() == true)
                {
                    ImageBrush brush = (ImageBrush)baseWindowTemplate.FindName("MyBgImg",this);
 
                    BitmapImage bitmap = new BitmapImage();
                    bitmap.BeginInit();
                    bitmap.UriSource = new Uri(dlg.FileName, UriKind.Absolute);
                    bitmap.EndInit();
                    brush.ImageSource = bitmap;
                }
            };
View Code

 

三、重写windows调整大小的方法,利用win32程序实现。
   此部分主要重写的DragMove、显示拖拉鼠标形状、还原鼠标形状、判断区域,改变窗体大小几个方面。
 
MediaElement循环播放问题

xaml中的代码:
<MediaElement Name="myMediaElement" Stretch="Fill" UnloadedBehavior="Manual" Source="{Binding CurrentElement.Src}" MediaEnded="myMediaElement_MediaEnded">
    </MediaElement>
 
后台的事件处理:
        
private void myMediaElement_MediaEnded(object sender, RoutedEventArgs e)
        {
            MediaElement me = (MediaElement)sender;
            me.Position = new TimeSpan(0);
            me.Play();
        }

 

其中,若是不设置UnloadedBehavior="Manual"属性的话,后台处理当播放完毕的时候不能继续进行播放。也就是到me.Play();

 处会报错误。

用一个程序打开另外一个程序C#

//打开! 

        private void ventuze_Click(object sender, RoutedEventArgs e)

        {

            //获取当前窗口句柄

            IntPtr handle = new WindowInteropHelper(this).Handle;

            string path = @"D:\twinflag_res\Ventuz\New.vpr";

            string openSoft = @"C:\Program Files\VentuzPro\VentuzPresenter.exe";

            app = System.Diagnostics.Process.Start(openSoft,path);

            prsmwh = app.MainWindowHandle;

            while (prsmwh == IntPtr.Zero)

            {

                prsmwh = app.MainWindowHandle;

            }

            //设置父窗口

            SetParent(prsmwh, handle);

            ShowWindowAsync(prsmwh, 3);//子窗口最大化

        }

 

//关闭

private void Window_Closed(object sender, System.EventArgs e)

  {

   if (app.CloseMainWindow()){

    app.Kill();

    app.Close();

   } 
View Code

 

WPF 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);
        }
    }
}
View Code

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

wpf中的画线问题,后台接受数据InkCanvas

后台操做代码:
/// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(10000);
            timer.Tick += new EventHandler(timer_Tick);
            timer.Start();
        }
 
        void timer_Tick(object sender, EventArgs e)
        {
            main();
            inv.Strokes = strokeCollection;
        }
 
        double i = 0, j = 0;
        StrokeCollection strokeCollection = new StrokeCollection();
 
        private void main()
        {
            StylusPoint stylusPoint = new StylusPoint(i,j);
            StylusPoint stylusPoint1 = new StylusPoint(i++, j++);
            StylusPointCollection stylusPointCollection = new System.Windows.Input.StylusPointCollection();
            stylusPointCollection.Add(stylusPoint);
            stylusPointCollection.Add(stylusPoint1);
            DrawingAttributes drawingAttributes = new DrawingAttributes();
            drawingAttributes.Color = Color.FromRgb(33,111,0);
            Stroke stroke = new Stroke(stylusPointCollection, drawingAttributes);
           
            strokeCollection.Add(stroke);
        }
    }
View Code

前台xaml代码:

<Window x:Class="WpfApplication7.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"> 
<Canvas x:Name="canvas">
        <InkCanvas x:Name="inv" Margin="0"/>
    </Canvas>
</Window>

 

wpf的binding大图片程序内存崩溃的大图片问题解决方法

  binding图片的高低像素就能够了,不用讲图片所有都赋值过去,这样看起来和以前像素也是同样。
如此binding以后就不会出现崩溃的现象了
  
public class ImageViewModel : BaseRegionViewModel
    {
        #region 构造器
        public ImageViewModel()
        {
        }
 
        #endregion
 
        #region 属性
 
        private BitmapImage _myBitmapImage;
       
        public BitmapImage MyBitmapImage
        {
            get { return _myBitmapImage; }
            set
            {
                _myBitmapImage = value;
                this.RaisePropertyChanged("MyBitmapImage");
            }
        }
 
        #endregion
 
        public override void CurrentElementChanged()
        {
            base.CurrentElementChanged();
            BitmapImage bitmap = new BitmapImage();
            bitmap.BeginInit();
            bitmap.StreamSource = new FileStream(CurrentElement.Src, FileMode.Open, FileAccess.Read);
            bitmap.DecodePixelHeight = Convert.ToInt32(CurrentRegion.Height);
            bitmap.DecodePixelWidth = Convert.ToInt32(CurrentRegion.Width);
            bitmap.CacheOption = BitmapCacheOption.OnLoad;
            bitmap.EndInit();
 
            bitmap.StreamSource.Dispose();
 
            MyBitmapImage = bitmap;
        }
 
        public override void ClearResource()
        {
            base.ClearResource();
        }
    }
View Code

 

wpf有时候按钮透明以后会出现虚框(tab键以后会出现)解决方法

<Style TargetType="Button" x:Key="btnHotelInfoStyle" >
            <Setter Property="Focusable" Value="false"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Name="border"
                    BorderThickness="0"
                    Padding="4,2"
                    BorderBrush="DarkGray"
                    CornerRadius="0"
                    Background="{TemplateBinding Background}">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

 

wpf 的converter传递复杂参数(摘抄)

Step 1

在WPF的C#代码文件中给定义复杂类型的变量,并给其赋值;

Sample code: List<User>lsUser=。。。。

Setp 2

在 C#代码对应的XAML 中将此复杂参数定义为资源;

Sample code:

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:c="clr-namespace:SDKSample"
  x:Class="SDKSample.Window1"
  Width="400"
  Height="280"
  Title="MultiBinding Sample">
        
  <Window.Resources>
    <c:lsUser x:Key="userList"/>
...
 
</Window.Resources>
 
这里的命名空间 C 是你的复杂参数所在的命名空间;
Step 3

 

  <UserControl.Resources>

    <app:UserManager x:Key="StaticUsers"/>

    <app:UserNameConverter  x:Key="UsrConverter"/>

  </UserControl.Resources>

<TextBox  Text="{Binding XXXX,Converter={StaticResource UsrConverter},

  ConverterParameter={StaticResource userList }}" />

 

Step 4 Converter 里对参数的使用

 

public class UserNameConverter : IValueConverter

{

    public object IValueConverter.Convert(object value, Type targetType,object parameter, CultureInfo culture)

    {

      List<User> usrs = parameter as List<User>;

      ...

    }

}
View Code

 

1、 若是 ObservableCollection<User> 是基础数据,能够将它们做为全局的变量,在 UserNameConverter 中直接使用 ObservableCollection<User> ,用不着使用 ConverterParameter。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  class  UserNameConverter : IValueConverter 
     public  object  IValueConverter.Convert( object  value, Type targetType, object  parameter, CultureInfo culture)
    
       // UserManager.Instance.Users 存储了全局的 ObservableCollection<User>
       // 也能够实现一些方法,用于将“12,34,56”返回“张三,李四,王五”
       return  UserManager.Instance.GetMultiNamesFromIds( value.ToString() );
     }
 
     public  object  IValueConverter.ConvertBack( object  value, Type targetType,  object  parameter, CultureInfo culture)
     {
       return  null ;
     }
}



2、 若是 ObservableCollection<User> 不能做为全局的,我的推荐的方式是避开使用将 ObservableCollection<User> 做为 ConverterParameter 的方法,改成使用字符串或 enum
参数,如

1
2
3
4
5
6
7
8
9
public  class  UserNameConverter : IValueConverter 
     public  object  IValueConverter.Convert( object  value, Type targetType, object  parameter, CultureInfo culture)
     {
       // UserManager.Instance 中存储了多个 ObservableCollection<User> ,根据参数返回不一样的 ObservableCollection<User>
       // parameter 是一个字符串或者是一个 enum
       return  UserManager.Instance.GetMultiNamesFromIds( parameter.ToString(), value.ToString() );
     }
}



3、 若是 ObservableCollection<User> 不能做为全局的,而又非要把它经过ConverterParameter 来传递, 因为在 XAML 中只能使用 StaticResources ,那么就只能在代码中来给 StaticResources 赋值了:

1
2
3
4
5
6
   < UserControl.Resources >
     < app:UserManager  x:Key = "StaticUsers" />
     < app:UserNameConverter   x:Key = "UsrConverter" />
   </ UserControl.Resources >
< TextBox   Text="{Binding XXXX,Converter={StaticResource UsrConverter},
   ConverterParameter={StaticResource StaticUsers}}" />



1
2
3
4
5
6
7
8
public  class  UserNameConverter : IValueConverter 
     public  object  IValueConverter.Convert( object  value, Type targetType, object  parameter, CultureInfo culture)
     {
       ObservableCollection<User> usrs = parameter  as  ObservableCollection<User>;
       ...
     }
}



1
2
3
4
public  class  UserManager : ObservableCollection<User>
{
 
}



在 UserControl 的 .cs 代码中(通常在是构造函数中):

1
2
UserManager =  this .Resources[ "StaticUsers" as  UserManager ;
UserManager.Add(User 实例);



4、 我的喜爱是不会使用上述 “三”的 UserManager 定义,
不使用继承,而是使用组合

1
2
3
4
public  class  UserManager 
{
    public   ObservableCollection<User> UserList{ get ; set ;}
}

 

1
2
3
4
5
6
   < UserControl.Resources >
     < app:UserManager  x:Key = "StaticUsers" />
     < app:UserNameConverter   x:Key = "UsrConverter" />
   </ UserControl.Resources >
< TextBox   Text="{Binding XXXX,Converter={StaticResource UsrConverter},
   ConverterParameter={StaticResource StaticUsers.UserList}}" />


不过好像又记得在 StaticResource 中是不能使用 属性路径 的,即 StaticUsers.UserList 是不能用的,
忘了啦。

PS:以上代码全手写,仅做思路参考之用。

 

Sorry , 发完贴又看了一遍,上述第三步有误,不用在 XAML 中定义 StaticUsers 的,在.cs代码中定义便可:

1
2
3
4
5
   < UserControl.Resources >
     < app:UserNameConverter   x:Key = "UsrConverter" />
   </ UserControl.Resources >
< TextBox   Text="{Binding XXXX,Converter={StaticResource UsrConverter},
   ConverterParameter={StaticResource StaticUsers}}" />

 

1
2
3
4
5
//在构造函数的 InitializeComponent(); 以前执行
ObservableCollection<User> usrs = ... 经过各类途径获得 ObservableCollection<User> ;
this .Resources.Add( "StaticUsers" , usrs );
 
InitializeComponent();
其实我本身几天前已经用静态变量搞定了,不过这样就没用到ConverterParameter了
微软搞了一个ConverterParameter,我想学习用一下这个东东而已,并且以为静态变量不是很好那种...

   public class IUserNameConverter : IValueConverter
    {
       public static ObservableCollection<User> TempUserList;

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
        }
    }

------------------------------

恩,仍是sunpire厉害啊

我上网找了半天,就是没有找到
this.Resources.Add("StaticUsers", usrs );
这一句关键代码啊

不过惋惜的是这句代码必定要在构造函数中写,问题是实际代码没看到过这样写的,
InitializeComponent()确定早运行过了,
实际业务数据确定是从服务器去取的,我总不能为了搞这个,从服务器取业务数据都写在InitializeComponent前面吧

也就是说ConverterParameter更多的只具备观赏价值,没有太多实用价值。

简单的说,复杂一点的转换器参数传递最好使用全局静态变量,避免使用ConverterParameter这个东西。

恩,明白,结贴。
WPF color、brush、string转换(摘抄)

//color转为brush:  
Brush br = new SolidColorBrush(Color.FromRgb(000));   
//string转Color  
(Color)ColorConverter.ConvertFromString((string)str);  
//Color转string((Color)value).ToString();  
string和Brush的转换  
Brush color = newSolidColorBrush((Color)ColorConverter.ConvertFromString((string)str));  
//Brush转string  
((Brush)value).ToString();  
//string转byte[]  
System.Text.UnicodeEncoding converter = newSystem.Text.UnicodeEncoding();  
byte[] stringBytes = converter.GetBytes(inputString);   
//byte[]转string  
System.Text.UnicodeEncoding converter = newSystem.Text.UnicodeEncoding();  
stringoutputString = converter.GetString(stringByte);   
    
1.由string的rgb数值"255,255,0"转换为color  
  
{  
  string[]  color_params = e.Parameter.ToString().Split(',');  
  byte color_R = Convert.ToByte(color_params[0]);  
  byte color_G = Convert.ToByte(color_params[1]);  
  byte color_B = Convert.ToByte(color_params[2]);  
}  
  
2.由颜色名称字符串("black") 转化为color  
  
 {  
  
  //ColorConverter c = new ColorConverter();  
  //object obj = c.ConvertFrom();  
  //Color color = (Color)obj;  
  Color color = Color.FromRgb(color_R, color_G, color_B);    
  
}  
  
3.将blend的 8位颜色值转为color  
  
        /// <summary>  
  
        /// 将blend的8位颜色值转为color  
        /// </summary>  
        /// <param name="colorName"></param>  
        /// <returns></returns>  
        public Color ToColor(string colorName)  
        {  
            if (colorName.StartsWith("#"))  
                colorName = colorName.Replace("#", string.Empty);  
            int v = int.Parse(colorName, System.Globalization.NumberStyles.HexNumber);  
            return new Color()  
            {  
                A = Convert.ToByte((v >> 24) & 255),  
                R = Convert.ToByte((v >> 16) & 255),  
                G = Convert.ToByte((v >> 8) & 255),  
                B = Convert.ToByte((v >> 0) & 255)  
            };  
        }  
View Code

 

 
wpf后台binding

public partial class ImageView : UserControl
    {
        ImageViewModel imageViewModel;
        public ImageView()
        {
            InitializeComponent();
            imageViewModel = this.DataContext as ImageViewModel;
            BindingBitmap();
        }
 
        private void BindingBitmap()
        {
            Binding binding = new Binding();
            binding.Source = imageViewModel;
            binding.Path = new PropertyPath("MyBitmapImage");
            BindingOperations.SetBinding(this.img, Image.SourceProperty, binding);
        }
    }   

 

自定义竖排textblock,前人基础(摘抄)

public class VerticalTextBlock : Control 
{ 
public VerticalTextBlock() 
{ 
IsTabStop = false; 
var templateXaml = 
@"<ControlTemplate " + 
#if SILVERLIGHT 
"xmlns='http://schemas.microsoft.com/client/2007' " + 
#else 
"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " + 
#endif 
"xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>" + 
"<Grid Background=\"{TemplateBinding Background}\">" + 
"<TextBlock x:Name=\"TextBlock\" TextAlignment=\"Center\"/>" + 
"</Grid>" + 
"</ControlTemplate>"; 
#if SILVERLIGHT 
Template = (ControlTemplate)XamlReader.Load(templateXaml); 
#else 
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(templateXaml))) 
{ 
Template = (ControlTemplate)XamlReader.Load(stream); 
} 
#endif 
} 

public override void OnApplyTemplate() 
{ 
base.OnApplyTemplate(); 
_textBlock = GetTemplateChild("TextBlock") as TextBlock; 
CreateVerticalText(_text); 
} 

private string _text { get; set; } 
private TextBlock _textBlock { get; set; } 

public string Text 
{ 
get { return (string)GetValue(TextProperty); } 
set { SetValue(TextProperty, value); } 
} 
public static readonly DependencyProperty TextProperty = DependencyProperty.Register( 
"Text", typeof(string), typeof(VerticalTextBlock), new PropertyMetadata(OnTextChanged)); 
private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 
{ 
((VerticalTextBlock)o).OnTextChanged((string)(e.NewValue)); 
} 
private void OnTextChanged(string newValue) 
{ 
CreateVerticalText(newValue); 
} 

private void CreateVerticalText(string text) 
{ 
_text = text; 
if (null != _textBlock) 
{ 
_textBlock.Inlines.Clear(); //清楚一遍数据,防止两条消息来临的时候发生错误
bool first = true; 
foreach (var c in _text) 
{ 
if (!first) 
{ 
_textBlock.Inlines.Add(new LineBreak()); 
} 
_textBlock.Inlines.Add(new Run { Text = c.ToString() }); 
first = false; 
} 
} 
} 

}
View Code

 

wpf中Button按钮和Enter关联(原+摘)

1.MouseButtonEventArgs 类定义在 System.Windows.Input命名空间中。含有方法GetPosition方法,此方法返回一个Point类型(这是定义在System.Windows命名空间内的结构体类型)的对象,表示鼠标坐标(相对于GetPosition参数的左上角)。含有ChangedButton属性,此属性“获取以该事件关联的按钮”(也就是是鼠标的左键,右键等)。

2.Button的 IsDefault 属性设置为true代表此按钮和Enter键关联;IsCancel 属性设置为true代表此按钮和Esc键关联。

若是给Button设置快捷键则能够以下:

<Button Name="btnEnter" Content="查询(_Q)" Width="200" Height="100" ></Button> 下划线加字母表明快捷键为Alt+Q。

可是若是Button在ToolBar里那么上面这种写法不行,须写为:

<ToolBar>

     <Button Name="btnWantCreate">

          <AccessText>新增(_N)</AccessText>

     </Button>

</ToolBar>

3.图片属性里德生成操做设置为SplashScreen,这样在显示本身程序前会先显示这个图片。

4.WPF中. 在Toolbar中连续使用Tab键时,其会循环与其中.如何跳出该循环呢, 很简单, 将TabBar的TabNavigation属性设置为Continue就能够了。

<ToolBar  KeyboardNavigation.TabNavigation="Continue"></ToolBar>

5.在XAML里限定泛型的类型(http://msdn.microsoft.com/zh-cn/library/ms750476.aspx):

      假定声明了如下 XAML 命名空间定义:

xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib"

     List<字符串>

     <scg:List x:TypeArguments="sys:String" ...> 实例化新的带有两个 String 类型参数的 List<T>。

 Dictionary<字符串,字符串>

 <scg:Dictionary x:TypeArguments="sys:String,sys:String" ...> 实例化新的带有两个 String 类型参数的 Dictionary<TKey, TValue>。

6.程序的Icon设置:  右键项目选择属性-->应用程序-->资源-->图标和清单-->图标  选中本身想要设置的Icon

Windows Presentation Foundation (WPF) 独立应用程序具备两种类型的图标:

一个程序集图标,此图标是在应用程序的项目生成文件中使用 <ApplicationIcon> 属性指定的。 此图标用做程序集的桌面图标。

注意:When debugging in Visual Studio, your icon may not appear due to the hosting process. If you run the executable, the icon will appear. 有关更多信息,请参见承载进程 (vshost.exe)。当调试的时候不显示图标,开始运行不调试时才显示.

每一个窗口各自具备的一个图标,此图标是经过设置 Icon 指定的。 对于每一个窗口,此图标在窗口的标题栏、任务栏按钮和 Alt-Tab 应用程序选择列表项中使用.

WPF 窗口老是显示一个图标。 若是没有经过设置 Icon 提供图标,WPF 将基于下列规则选择要显示的图标:

1. 使用程序集图标(若是已指定)。

2. 若是未指定程序集图标,则使用默认的 Microsoft Windows 图标。

若是使用 Icon 指定自定义窗口图标,能够经过将 Icon 设置为 null 来还原默认的应用程序图标。

相关文章
相关标签/搜索