sticky
Windows Presentation Foundation 常见问题 RRS feed

全部回复

  •   

    1. 程序模型  [回到顶端]

      

    1.1 WPF中分辨率是与设备无关的吗?  [回到顶端]

      

    这是WPF中一个比较不太好理解的概念。感谢Charles Petzold先生准确并且权威的解释:

     

    Windows用户最终关心到显示器分辨率的设置,这是由显示器applet造成的。一般来说,我们认为分辨率是96 或是120 DPI,当然也可以设置更高。这种情况在以往很多个Windows很多个版本中,并且在Vista系统中也没有改变。

      

    我们假定分辨率是和实际的显示器分辨率无关的,当然用户可以自己设置两者的值相同,但是我们一般很少这么做。再次申明一下,这种情况在Vista系统中也是一样的。

      

    WPF编程是设备无关性的,具体来说是每英寸96个单位。假如显示器的分辨率为96 DPI,那么WPFDIU(Device Independent Unit)则与一个像素一致。假如显示器的分辨率为120 DPI,那么后者则为1/3像素。

      

    从技术上来说,这些没有本质上的改变。几十年来,Windows不是适应这些显示器的分辨率,就是与这些分辨率上相匹配。其实整个的进程在WPF中比起以前的Windows API有点更方便,但是从技术角度来说,还是一样的。

      

    WPFDIU主要是对编程人员来说的,一般的用户并不关心这个,用户并不要求适应WPFDIUDevice Independent Unit)。

      

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/996269d6-d619-4f82-94b1-68ad60fba43b/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/33bdb15c-a04c-4c17-85e1-6c2802f80b90/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/cf904ef5-ab22-46e3-8f95-0832a1810823/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/eca90b89-0a09-40b8-9d23-dc79066daae3/

     

    2009年7月9日 3:47
  •  

    1.2 需要手动清除由位图或是图像利用的资源吗?  [回到顶端]

      

    不像在Windows FormWPF实现了一个统一的模型来管理诸如画刷,文本,位图,视频和音频等等。就拿图像控件来说,如果它是和视觉树无连接的话,那么它的潜在的非托管的资源比如位图就会被MIL (Media Integration Layer)释放掉。如果这里没有强有力的管理工具的话,这些托管资源就会被垃圾回收。但是由你程序生成的托管资源,你就得自己去执行Dispose方法来释放它。

      

    如果你确实发现内存泄漏或是怀疑一些对象在程序中泄漏,你可以用windbg或者CLR profiler这些工具来追踪这这些问题。

      

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/c0cbb4a0-38d8-484b-8c56-9a2ca04392df/

     

    2009年7月9日 3:47
  •  

    1.3 有可能改变WPF内置的Windows窗体的Z-顺序吗?  [回到顶端]

      

    当我们把 一个Windows Forms控件放在一个WPF窗体中,这个Windows Forms控件一般都会呈现在WPF可视树的最顶层。这种情况在我们利用HwndHost/WindowsFormsHost载入Win32/Windows Form控件时得考虑。

      

    更多这方面背景的信息,请参照以下MSDN文章:

    http://msdn2.microsoft.com/en-us/library/aa970688.aspx

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/e307df92-7779-40f8-a6d9-918b3252ba1c/

     

    2009年7月9日 3:49
  •  

    1.4 怎样在ASP.NET页面中内置一个WPF/XBAP程序?  [回到顶端]

      

    目前XBAPASP.NET之间的集成和互操作性是非常有限的,幸运的我们可以使用HTML框架来载入XBAP程序,使用cookie或是query字符串在ASP.NETXBAP之间通信。并且,如果我们需要富集成和富互操作性的特性的话,我们可以使用SilverlightWPF的一个子集),它是专门为WEB环境下设计并优化的。

      

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/29dd2c45-542d-487b-a8cb-bbff66d4e5c4/

     

    2009年7月9日 3:56
  •    

    1.5 怎样在WPF中使用Windows 窗体的控件(也就是水晶报表)?  [回到顶端]

      

    我们可以使用WindowsFormsHost元素在WPF中载入一个Windows Forms控件,下面的这个例子展示了如何载入一个CrystalReportViewer元素:

     

    <Window

       x:Class="ForumProjects.MainWindow"

       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

       xmlns:c="clr-namespace:CrystalDecisions.Windows.Forms;assembly=CrystalDecisions.Windows.Forms"

       Title="MainWindow" Width="800" Height="600">

        <WindowsFormsHost>

            <c:CrystalReportViewer x:Name="MyCrystalReportViewer" Width="300" Height="300"/>

        </WindowsFormsHost>

    </Window>

      

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/8b9a13eb-f633-4b5f-886f-e2d2196b4b10/

     

    2009年7月9日 3:57
  •  

    1.6 有可能使XAML衍生的类子类化吗?  [回到顶端]

     

    当前的版本的WPF并不支持使XAML衍生的类可能派生子类,如果这个子类同样使用XAML的话。为了使用基类继承由XAML产生的子类,我们必须在代码全部实现基类。

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/70987719-8009-4319-9fb6-4ff270d1d69c/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/8dfcc6a8-edb2-4896-851d-df191d596e9f/

     

    2009年7月9日 3:57
  •  

    2. 基本服务(线程,键盘/鼠标操作以及依赖属性)  [回到顶端]

     

    2.1 WPF的Application.DoEvents在哪里?   [回到顶端]

     

    不像在Windows Forms中一样,WPF并不提供一个Application.DoEvents()这个的相似的功能允许开发者启动嵌套消息泵(nesting message pumping),因为嵌套消息泵会产生一些难以发现的问题,比如说产生代码重入。更重要的是,WPF在布局和在可视树中遍历元素的时候会禁用消息泵。

     

    但是在一些比较少的情况下使用消息泵是很有用的,比如说在单元测试,下面的这个帮助类提供在WPF中一个类似Application.DoEvents()的功能。

     

    /// <summary>

        ///用一些新增功能封装WPF dispatcher

        /// </summary>

        public class DispatcherHelper

        {

            private static DispatcherOperationCallback exitFrameCallback = new

                 DispatcherOperationCallback(ExitFrame);

            /// <summary>

            ///处理所有当前UI消息队列

            /// </summary>

            public static void DoEvents()

            {

                //创建一个级连消息队列

                DispatcherFrame nestedFrame = new DispatcherFrame();

                //当获取调用的时候,为当前的消息队列分配回调,这个回调会结束消息循环。注意这个回周的优先级应该比UI事件消息的优先级低

                DispatcherOperation exitOperation = Dispatcher.CurrentDispatcher.BeginInvoke(

                DispatcherPriority.Background, exitFrameCallback, nestedFrame);

                //压入级连消息队循环,这个消息循环会立即处理消息队列中最左边的消息

                Dispatcher.PushFrame(nestedFrame);

                //"exitFrame"回调没有完成,就中断它

                if (exitOperation.Status != DispatcherOperationStatus.Completed)

                {

                    exitOperation.Abort();

                }

            }

            private static Object ExitFrame(Object state)

            {

                DispatcherFrame frame = state as DispatcherFrame;

                //退出级连消息循环

                frame.Continue = false;

                return null;

            }

    }

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/a82b4d6a-6245-46c4-98d1-2c88b0a9c836/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/6fce9b7b-4a13-4c8d-8c3e-562667851baa/

     

    2009年7月9日 3:58
  •  

    2.2 怎样在线程间访问WPF的控件?  [回到顶端]

     

    像很多的UI框架比如Windows Forms一样,WPF也是使用单线程模型。这意味着你只可以访问一个由当前线程产生的DispatcherObject.Windows Forms中,每一个控件都会执行SynchronizeInvoke接口,这个接品暴露了一些方法比如InvokeBeginInvoke去执行普通的线程同步模型,这种情况下我们可以访问其他线程中的控件. WPF中,我们也可以做类似的事情,这些操作方法封装在一个叫做Dispatcher的类中,Dispatcher类以一种WPF特有的方式去执行这种类型的线程同步模型。

     

    下面的这个例子说明当调用者在另一个线程中时怎样改变TextBoxText属性:

      

    //在另一线程中重新设置TextBoxText属性

    textBox.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>

    {

        textBox.Text = "New text";

    }));

      

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/23df7288-fc38-4bde-9eec-0dce8e39cd45/

     

    2009年7月9日 3:58
  •   

    3. 控件  [回到顶端]

      

    3.1 怎样把TreeView中所以节点全部展开?  [回到顶端]

     

    不同于Windows Forms,当前WPF版本没有提供一个直接的方法可以把TreeView控件所有的节点都展开。一般来说,在WPF中有两种方法可以实现这个功能。第一种方法就像下面例子一样使用样式展开所有节点:

     

    <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <Page.Resources>

            <XmlDataProvider x:Key="treeData" XPath="*">

                <x:XData>

                    <Items Name="Items" xmlns="">

                        <Item1/>

                        <Item2>

                            <Item22/>

                            <Item12/>

                            <Item13>

                                <Item131/>

                                <Item131/>

                            </Item13>

                        </Item2>

                    </Items>

                </x:XData>

            </XmlDataProvider>

            <HierarchicalDataTemplate ItemsSource="{Binding XPath=child::*}"

                                 x:Key="template">

                <TextBlock Name="textBlock" Text="{Binding Name}"/>

        </HierarchicalDataTemplate>

        </Page.Resources>

        <TreeView ItemTemplate="{StaticResource template}"

               ItemsSource="{Binding Source={StaticResource treeData}}">

            <TreeView.ItemContainerStyle>

                <!--Using style setter to set the TreeViewItem.IsExpanded property to true, this will be applied

          to all TreeViweItems when they are generated-->

                <Style TargetType="{x:Type TreeViewItem}">

                    <Setter Property="IsExpanded" Value="True"/>

                </Style>

            </TreeView.ItemContainerStyle>

        </TreeView>

    </Page>

      

    有些时候,你需要自己写代码去实现这个功能,下面的是用C#代码写成的一个帮助类可以展开TreeView控件中所有的节点:

     

      public static class TreeViewHelper

      {

            public static void ExpandAll(TreeView treeView)

            {

                ExpandSubContainers(treeView);

            }

            private static void ExpandSubContainers(ItemsControl parentContainer)

            {

                foreach (Object item in parentContainer.Items)

                {

                    TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;

                    if (currentContainer != null && currentContainer.Items.Count > 0)

                    {

                        //展开当前节点

                        currentContainer.IsExpanded = true;

                        if (currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)

                        {

                            //如果装入当前节点的容器还没有生成好,我们应该等待到它一直生成完成。

                            currentContainer.ItemContainerGenerator.StatusChanged += delegate

                            {

                                ExpandSubContainers(currentContainer);

                            };

                        }

                        else

                        {

                            //如果装入当前节点的容器已经生成,我们可以直接展开它。

                            ExpandSubContainers(currentContainer);

                        }

                    }

                }

            }

    }

     

    上面这个例子的关键之处就是要确认当前的TreeViewItem的容器已经生成了,所有你可以安全的展开当前节点的所有的子节点。这就是为什么你需要采用递归的方法(当前的节点ItemContainerGenerator状态是GeneratorStatus.ContainersGenerated.时)。

      

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/11e28533-57dd-4b51-bc07-627fbde19a8d/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/08b9d0b4-e9fc-4081-aedf-6230508cf840/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/f1fadc05-bced-49b1-af61-a68ce80980ff/

     

    2009年7月9日 3:59
  •  

    3.2 怎样在ListBox/ListView中实现多项选择?  [回到顶端]

     

    Lasso selection)在图形编辑程序中用得非常普遍,这种方法可以方便的选中屏幕上的多个对象。下面的这个例子展示了针对WPF的控件ListBox/ListView实现这个功能。

     

    <ListBox Name="listBox"

            Width="200"

            Height="200"

            SelectionMode="Multiple">

            <ListBox.Resources>

                <Style TargetType="{x:Type ListBoxItem}">

                    <EventSetter Event="ListBoxItem.PreviewMouseLeftButtonDown"

                      Handler="ListBoxItem_PreviewMouseLeftButtonDown"/>

                    <EventSetter Event="ListBoxItem.PreviewMouseUp"

                      Handler="ListBoxItem_PreviewMouseUp"/>

                    <EventSetter Event="ListBoxItem.PreviewMouseMove"

                      Handler="ListBoxItem_PreviewMouseMove"/>

                </Style>

            </ListBox.Resources>

            <x:Type TypeName="DependencyObject"/>

            <x:Type TypeName="Visual"/>

            <x:Type TypeName="UIElement"/>

            <x:Type TypeName="FrameworkElement"/>

            <x:Type TypeName="Control"/>

        </ListBox>

     

    public partial class Window1 : Window

        {

            //这个字段用于显示ListBox是否是鼠标选择状态

            private Boolean inMouseSelectionMode = false;

            //这个字段用于与当前鼠标选择的项保持联系

            private List<ListBoxItem> selectedItems = new List<ListBoxItem>();

            public Window1()

            {

                InitializeComponent();

            }

            private void ListBoxItem_PreviewMouseUp(object sender, MouseButtonEventArgs e)

            {

                //若鼠标松开,则关闭"inMouseSelectionMode"

                inMouseSelectionMode = false;

            }

            private void ListBoxItem_PreviewMouseMove(object sender, MouseEventArgs e)

            {

                ListBoxItem mouseOverItem = sender as ListBoxItem;

                if (mouseOverItem != null && inMouseSelectionMode && e.LeftButton == MouseButtonState.Pressed)

                {

                    mouseOverItem.Background = SystemColors.HighlightBrush;

                    //高亮显示当前位于鼠标下方的项

                    mouseOverItem.SetValue(TextElement.ForegroundProperty, SystemColors.HighlightTextBrush);

                    if (!selectedItems.Contains(mouseOverItem))

                    {

                        selectedItems.Add(mouseOverItem);

                    }

                }

            }

            private void ListBoxItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)

            {

                //当鼠标点击时,我们需要消除所有先前选中的项

                listBox.SelectedIndex = -1;

                inMouseSelectionMode = true;

                foreach (ListBoxItem item in selectedItems)

                {

                    item.ClearValue(ListBoxItem.BackgroundProperty);

                    item.ClearValue(TextElement.ForegroundProperty);

                }

                selectedItems.Clear();

            }

    }

      

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/98f3ab12-a82e-4f35-a9a1-ee0c180350ac/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/6b9c26fa-22a6-4882-aa94-1974aa7f29ce/

     

    2009年7月9日 4:07
  •  

    3.3 怎样实现单选列表控件?  [回到顶端]

      

    WPF没有像ASP.NET提供一个RadioButtonList的控件。幸运的是,我们可以利用WPF样式和模板的强大功能,用纯粹的XAML代码实现这个功能,下面是一个在XAMLPad可运行的例子:

     

    <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

         xmlns:s="clr-namespace:System;assembly=mscorlib">

        <Page.Resources>

            <x:Array Type="{x:Type s:String}" x:Key="data">

                <s:String>Option1</s:String>

                <s:String>Option2</s:String>

                <s:String>Option3</s:String>

            </x:Array>

        </Page.Resources>

        <StackPanel DataContext="{StaticResource data}">

            <TextBlock Margin="5">

          <TextBlock Text="Current Option:"/>

          <TextBlock Text="{Binding /}"/>

        </TextBlock>

            <ListBox

           ItemsSource="{Binding}"

           IsSynchronizedWithCurrentItem="True"

           Width="240"

           Height="60"

           HorizontalAlignment="Left">

                <ListBox.ItemContainerStyle>

                    <Style TargetType="{x:Type ListBoxItem}">

                        <Setter Property="Template">

                            <Setter.Value>

                                <ControlTemplate TargetType="{x:Type ListBoxItem}">

                                    <RadioButton

                       IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}}"

                       Content="{TemplateBinding Content}"/>

                                </ControlTemplate>

                            </Setter.Value>

                        </Setter>

                    </Style>

                </ListBox.ItemContainerStyle>

            </ListBox>

        </StackPanel>

    </Page>

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/ea751358-6919-4925-8bb2-965f5f65d612/

     

    2009年7月9日 4:07
  •  

    3.4 怎样使得在一个Expander列中同时只能有一个Expander是展开的?  [回到顶端]

     

    如果你对Outlook左边的工具栏熟悉的话,你会发现ExpanderList是一个非常有用的控件类型,你可以把Expander控件放在ListBox中,把ExpanderIsExpanded属性与ListBoxItemIsSelected属性绑定起来,再利用ListBox默认只能选中一项的特性,我们可以使得同一时间只能有一个Expander是展开的。下面是一个在XAMLPad可运行的例子

     

    <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <ListBox>

            <ListBox.Resources>

                <Style TargetType="{x:Type Expander}">

                    <Setter

               Property="IsExpanded"

               Value="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"/>

                </Style>

            </ListBox.Resources>

            <ListBox.Template>

                <ControlTemplate TargetType="{x:Type ListBox}">

                    <ItemsPresenter/>

                </ControlTemplate>

            </ListBox.Template>

            <ListBox.ItemContainerStyle>

                <Style TargetType="{x:Type ListBoxItem}">

                    <Setter Property="Template">

                        <Setter.Value>

                            <ControlTemplate TargetType="{x:Type ListBoxItem}">

                                <ContentPresenter Content="{TemplateBinding Content}"/>

                            </ControlTemplate>

                        </Setter.Value>

                    </Setter>

                </Style>

            </ListBox.ItemContainerStyle>

            <Expander Background="Gray" Width="243" Header="Expander1">

                <StackPanel>

                    <RadioButton Content="Eat Me" GroupName="Two"/>

                    <RadioButton Content="Eat Pork" GroupName="Two"/>

                    <RadioButton Content="Eat at Joe's" GroupName="Two"/>

                </StackPanel>

            </Expander>

            <Expander Background="Gray" Width="243" Header="Expander2">

                <StackPanel>

                    <RadioButton Content="Pork" GroupName="Two"/>

                    <RadioButton Content="Beef" GroupName="Two"/>

                    <RadioButton Content="Chicken" GroupName="Two"/>

                </StackPanel>

            </Expander>

            <Expander Background="Gray" Width="243" Header="Expander3">

                <StackPanel>

                    <RadioButton Content="Grill" GroupName="Two"/>

                    <RadioButton Content="Bake" GroupName="Two"/>

                    <RadioButton Content="Fry" GroupName="Two"/>

                </StackPanel>

            </Expander>

        </ListBox>

    </Page>

      

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/d0a66a22-db2c-439e-8938-3c7f8160185b/

     

    2009年7月9日 4:08
  •  

    3.5 怎样实现在TreeView中父节点选中时其所有子节点自动选中?  [回到顶端]

     

    我们可以用数据绑定实现这个功能。先创建一个含有boolean类型属性的类,然后用这个属性绑定CheckedBoxIsChecked属性,并且用一个集合属性绑定TreeViewItem的所有字节点。当一个TreeViewItemCheckBox状态改变的时候,这个节点所有的子节点状态也会由这个boolean类型的属性改变。下面是一个相关例子:

     

     <Window.Resources>

            <HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding Children}">

                <StackPanel Orientation="Horizontal">

                    <CheckBox IsChecked="{Binding IsChecked}"/>

                    <TextBlock Text="{Binding Text}"/>

     

                </StackPanel>

            </HierarchicalDataTemplate>

        </Window.Resources>

    <TreeView ItemsSource="{Binding Nodes, ElementName=Window}"/> 

    public partial class MainWindow : Window

        {

            private ObservableCollection<Node> nodes;

            public MainWindow()

            {

                this.nodes = new ObservableCollection<Node>()

            {

                new Node(){Text="Node A"},

                new Node(){Text="Node B"},

            };

                this.Nodes[0].Children.Add(new Node() { Text = "Node C" });

                this.Nodes[0].Children.Add(new Node() { Text = "Node D" });

                this.Nodes[1].Children.Add(new Node() { Text = "Node E" });

                this.Nodes[1].Children.Add(new Node() { Text = "Node F" });

                InitializeComponent();

            }

            public ObservableCollection<Node> Nodes

            {

                get

                {

                    return nodes;

                }

            }

        }

        public class Node : INotifyPropertyChanged

        {

            ObservableCollection<Node> children = new ObservableCollection<Node>();

            string text;

            bool isChecked;

            public ObservableCollection<Node> Children

            {

                get { return this.children; }

            }

            public bool IsChecked

            {

                get {

                    return this.isChecked;

                }

                set

                {

                    this.isChecked = value;

                    RaisePropertyChanged("IsChecked");

                }

            }

            public string Text

            {

                get { return this.text; }

                set

                {

                    this.text = value;

                    RaisePropertyChanged("Text");

                }

            }

            public event PropertyChangedEventHandler PropertyChanged;

            private void RaisePropertyChanged(string propertyName)

            {

                if (this.PropertyChanged != null)

                    this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

                if (propertyName == "IsChecked")

                {

                    foreach (Node child in this.Children)

                        child.IsChecked = this.IsChecked;

                }

            }

    }

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/9fd804d5-050c-4b94-b877-a23d81782fce/

      

    2009年7月9日 4:08
  •   

    3.6 ContentPresenter是什么?  [回到顶端]

     

    ContentPresenter是以一种WPF的方式为元素和控件提供的内容模型。ContentPresenter从定义上来是为了呈现内容,这些内容可以包括文本,图像,几何图形,甚至是CLR对象,XML,以及从从数据库中得来的数据。WPF利用数据模板正确的呈现这些容。ContentPresenter内建了一系列的数据模板来呈现元素,字符串,XML数据,文档等等。你可以视具体要求自己创建DataTemplateDataTemplateSelector来重写ContentPresenter内建的数据模板,当然也可以使用默认的。举例来说,为了显示文本你可以像下面代码说明的那样做:

     

    <ContentPresenter Content="WPF"/>

      

    显示UIElements可以简单这样做:

     

    <ContentPresenter>

            <ContentPresenter.Content>

                <Ellipse Width="120" Height="60" Fill="Red"/>

            </ContentPresenter.Content>

    </ContentPresenter>

     

    显示XML数据可以这样做:

     

    <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

        <Page.Resources>

            <XmlDataProvider x:Key="xmlData" XPath="XmlData">

                <x:XData>

                    <XmlData Content="WPF" xmlns=""/>

                </x:XData>

            </XmlDataProvider>

        </Page.Resources>

        <ContentPresenter

         Content="{Binding XPath=@Content, Source={StaticResource xmlData}}"/>

    </Page>

     

    显示文档可以这样做:

     

      <ContentPresenter>

            <ContentPresenter.Content>

                <FlowDocument>

                    <Paragraph>WPF</Paragraph>

                </FlowDocument>

            </ContentPresenter.Content>

    </ContentPresenter>

     

    ContentPresenter之所以能正确的显示这些数据是因为它内建了很多的数据模板,这些模板机制会根据数据的类型选择相对应的模板。

    你可以自己创建ContentPresenterContentTemplateContentTemplateSelector来重写综默认的模板,下面的例子展示了怎样显示一个CLR类型的对象的Namespace属性。

     

    <ContentPresenter>

            <ContentPresenter.Content>

                <x:Type TypeName="ContentPresenter"/>

            </ContentPresenter.Content>

            <ContentPresenter.ContentTemplate>

                <DataTemplate>

                    <TextBlock Text="{Binding Namespace}"/>

        </DataTemplate>

            </ContentPresenter.ContentTemplate>

    </ContentPresenter>

      

    利用ContentPresenter提供的便利,你可以在UI上展示任何类型的数据。通常ContentPresenter是内置在控件模板中,用来展示由控件模板产生的元素树中的内容。

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/2b389813-ebb5-450e-8ccd-c422e519354a/

     

    2009年7月9日 4:09
  •  

    4文本和文档  [回到顶端]

     

    4.1 RichTextBox中怎样实现查找和替换?  [回到顶端]

      

    不像Windows Forms一样,当前版本的WPF没有直接包含在RichTextBox中的搜索功能。幸运的是我们可以使用由Framework API提供的TextRangeTextPointer元素来创建自定义的RichTextBox搜索和替换特性。下面是一个例子:

     

    /// <summary>

        /// 这个类展示所有可能的搜索选项

        /// </summary>

        [Flags]

        public enum FindOptions

        {

            /// <summary>

            /// 不区分大小写

            /// </summary>

            None = 0x00000000,

            /// <summary>

            /// 区分大小写

            /// </summary>

            MatchCase = 0x00000001,

            /// <summary>

            /// 整字搜索

            /// </summary>

            MatchWholeWord = 0x00000002,

        }

        /// <summary>

        /// 这个类封装查找和替换操作<see cref="FlowDocument"/>.

        /// </summary>

        public sealed class FindAndReplaceManager

        {

            private FlowDocument inputDocument;

            private TextPointer currentPosition;

            /// <summary>

            /// <see cref="FindReplaceManager"/>

            /// 初始化一个特殊的类的实例<see cref="FlowDocument"/>

            /// </summary>

            /// <param name="inputDocument">输入文档</param>

            public FindAndReplaceManager(FlowDocument inputDocument)

            {

                if (inputDocument == null)

                {

                    throw new ArgumentNullException("documentToFind");

                }

                this.inputDocument = inputDocument;

                this.currentPosition = inputDocument.ContentStart;

            }

            /// <summary>

            /// 获取或设置位置偏移<see cref="FindReplaceManager"/>

            /// </summary>

            public TextPointer CurrentPosition

            {

                get

                {

                    return currentPosition;

                }

                set

                {

                    if (value == null)

                    {

                        throw new ArgumentNullException("value");

                    }

                    if (value.CompareTo(inputDocument.ContentStart) < 0 ||

                        value.CompareTo(inputDocument.ContentEnd) > 0)

                    {

                        throw new ArgumentOutOfRangeException("value");

                    }

                    currentPosition = value;

                }

            }

            /// <summary>

            /// 找到下一个输入字符的匹配

            /// </summary>

            /// <param name="input">全字搜索字符</param>

            /// 这个方法会提高使当前位置<see cref="CurrentPosition"/> 到下一个位置

            /// </remarks>

            public TextRange FindNext(String input, FindOptions findOptions)

            {

                TextRange textRange = GetTextRangeFromPosition(ref currentPosition, input, findOptions);

                return textRange;

            }

            /// <summary>

            /// 使用正则表达式替换一个字符串

            /// </summary>

            /// <param name="input">全字搜索的字符串.</param>

            /// <param name="replacement">替换字符串</param>

            /// <param name="findOptions"> 搜索选项</param>

            /// <returns><see cref="TextRange"/> 替换字符串的实例</returns>

            /// <remarks>

            /// 这个方法会提高使当前位置<see cref="CurrentPosition"/> 到下一个位置

            /// </remarks>

            public TextRange Replace(String input, String replacement, FindOptions findOptions)

            {

                TextRange textRange = FindNext(input, findOptions);

                if (textRange != null)

                {

                    textRange.Text = replacement;

                }

                return textRange;

            }

            /// <summary>

            /// 用一个特殊的输入字符,替换所有匹配特殊标准的字条串

            /// </summary>

            /// <param name="input">全字搜索的字符串</param>

            /// <param name="replacement">替换的字符串</param>

            /// <param name="findOptions"> 搜索选项</param>

            /// <param name="action">每一个输入字符中相匹配的操作</param>

            /// <returns>替换发生的次数</returns>

            /// <remarks>

            /// 这个方法会提高使当前位置<see cref="CurrentPosition"/> 到下一个位置

            /// </remarks>

            public Int32 ReplaceAll(String input, String replacement, FindOptions findOptions, Action<TextRange> action)

            {

                Int32 count = 0;

                currentPosition = inputDocument.ContentStart;

                while (currentPosition.CompareTo(inputDocument.ContentEnd) < 0)

                {

                    TextRange textRange = Replace(input, replacement, findOptions);

                    if (textRange != null)

                    {

                        count++;

                        if (action != null)

                        {

                            action(textRange);

                        }

                    }

                }

                return count;

            }

      

     (接下帖) 

       

    2009年7月9日 4:23
  •    

            /// <summary>

            /// 找到相对应的实例<see cref="TextRange"/>

            /// 在特定的位置呈现输入字符串

            /// </summary>

            /// <param name="position">当前文字位置</param>

            /// <param name="textToFind">输入文字</param>

            /// <param name="findOptions">搜索选项</param>

            /// <returns>

            /// 在一个容器中显示匹配的字符串

            /// </returns>

            public TextRange GetTextRangeFromPosition(ref TextPointer position,

                                                      String input,

                                                      FindOptions findOptions)

            {

                Boolean matchCase = (findOptions & FindOptions.MatchCase) == FindOptions.MatchCase;

                Boolean matchWholeWord = (findOptions & FindOptions.MatchWholeWord)

                                                            == FindOptions.MatchWholeWord;

                TextRange textRange = null;

                while (position != null)

                {

                    if (position.CompareTo(inputDocument.ContentEnd) == 0)

                    {

                        break;

                    }

                    if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)

                    {

                        String textRun = position.GetTextInRun(LogicalDirection.Forward);

                        StringComparison stringComparison = matchCase ?

                            StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase;

                        Int32 indexInRun = textRun.IndexOf(input, stringComparison);

                        if (indexInRun >= 0)

                        {

                            position = position.GetPositionAtOffset(indexInRun);

                            TextPointer nextPointer = position.GetPositionAtOffset(input.Length);

                            textRange = new TextRange(position, nextPointer);

                            if (matchWholeWord)

                            {

                                if (IsWholeWord(textRange)) //测试是否textRange代表一个单词

                                {

                                    //若发现全字搜索选项,直接结束循环

                                    position = position.GetPositionAtOffset(input.Length);

                                    break;

                                }

                                else

                                {

                                    //若未发现全字搜索选项,进入下一个递归循环

                                    position = position.GetPositionAtOffset(input.Length);

                                    return GetTextRangeFromPosition(ref position, input, findOptions);

                                }

                            }

                            else

                            {

                                //若发现不是全字搜索选项,直接结束循环

                                position = position.GetPositionAtOffset(input.Length);

                                break;

                            }

                        }

                        else

                        {

                            //若匹配没有发现,跳入到"textRun"位置后

                            position = position.GetPositionAtOffset(textRun.Length);

                        }

                    }

                    else

                    {

                        //若当前位置没有呈现文本位置,进入下一个内容位置

                        //这里实际上是忽略文字的格式

                        position = position.GetNextContextPosition(LogicalDirection.Forward);

                    }

                }

                return textRange;

            }

            /// <summary>

            /// 判断字符是否正确

            /// 这里只有下划线,字母,数字认为是正确的

            /// </summary>

            /// <param name="character">character specified</param>

            /// <returns>Boolean值判断特定的字符是否正确</returns>

            private Boolean IsWordChar(Char character)

            {

                return Char.IsLetterOrDigit(character) || character == '_';

            }

            /// <summary>

            /// 判断在某一实例中是否是一单词<see cref="TextRange"/>

            /// </summary>

            /// <returns>测试结果</returns>

            private Boolean IsWholeWord(TextRange textRange)

            {

                Char[] chars = new Char[1];

                if (textRange.Start.CompareTo(inputDocument.ContentStart) == 0 || textRange.Start.IsAtLineStartPosition)

                {

                    textRange.End.GetTextInRun(LogicalDirection.Forward, chars, 0, 1);

                    return !IsWordChar(chars[0]);

                }

                else if (textRange.End.CompareTo(inputDocument.ContentEnd) == 0)

                {

                    textRange.Start.GetTextInRun(LogicalDirection.Backward, chars, 0, 1);

                    return !IsWordChar(chars[0]);

                }

                else

                {

                    textRange.End.GetTextInRun(LogicalDirection.Forward, chars, 0, 1);

                    if (!IsWordChar(chars[0]))

                    {

                        textRange.Start.GetTextInRun(LogicalDirection.Backward, chars, 0, 1);

                        return !IsWordChar(chars[0]);

                    }

                }

                return false;

            }

    }

      

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/3d105d27-bddc-4a2f-8ee7-c74aa9c2945c/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/ff3c9cae-694d-4eeb-b265-55fa173c5e69/

     

    2009年7月9日 4:24
  •  

    5.样式  [回到顶端]

     

    5.1 当样式化按钮时,为什么在Windows XP 和Windows Vista下会有不同的效果?  [回到顶端]

     

    当你像下面这样为Button控件制定样式时:

     

        <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

            <Button>

                <Button.Style>

                    <Style TargetType="Button">

                        <Style.Triggers>

                            <Trigger Property="IsMouseOver" Value="true">

                                <Setter Property="Background" Value="Yellow"/>

                            </Trigger>

                        </Style.Triggers>

                    </Style>

                </Button.Style>

            </Button>

        </Page>

     

    你会发现IsMouseOver触发器在Windows Vista系统下不能正常运行,而在Windows XP却能正常运行。

     

    原因就是ButtonChromeWindows XP系统中是在PresentationFramwork.Luna程序集中执行的,而在Windows Vista系统中却是由PresentationFramwork.Aero程序集来执行的.但在Windows Vista系统中与RenderMouseOver的属性有不同的交互性。在Windows XP中,是由简单的重写ButtonChrome.OnRender()这个方法再高亮度化边框实现这个触发器的。而在Windows Vista中,它会额外的画一个高亮度的背景来模仿Vista Aero主题,而这个主题会掩盖你由样式触发器产生的背影。

     

    为了避免这个问题,你最好去自己重写Button默认的控件模板。

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/0bcc8186-8762-40ac-affe-19ed6098fb90/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/87ae4875-deae-4b64-875d-fe5464fca554/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/7dc11927-54e6-49e9-b137-45287fdf0bb1/

     

    2009年7月9日 4:24
  •  

    5.2 怎样重新制定Popup样式和模板?  [回到顶端]

     

    Popup继承于FrameworkElement元素,所以它也支持Framework元素级别的服务,比如说样式化。这样我们可以像其他继承于FrameworkElementr的元素一样通过设置它Style属性来设置它的样式。但是Popup不支持控件模板,为了使Popup支持这个功能,你可能把一个Control元素放在Popup中,然后像下面一样重写这个Control的控件模板:

     

    <Popup

       IsOpen="True"

       AllowsTransparency="True">

            <Control>

                <Control.Template>

                    <ControlTemplate>

                        <Border CornerRadius="5" BorderThickness="1" BorderBrush="Black">

                            <TextBlock Text="Template For Popup"/>

            </Border>

                    </ControlTemplate>

                </Control.Template>

            </Control>

    </Popup>

     

    另外一种方法是我们可以把ContentPresenter放在Popup中使其支持模板化。

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/6222fd59-9b42-4f0a-b257-b2744ce6cfd7/

     

    2009年7月9日 4:25
  •  

    5.3 在WPF中怎样自定义窗体?  [回到顶端]

     

    我们知道WPF的控件可以通过控件模板改变它的外观,但是实现上WPF窗体也可能这样。下面的例子展示怎样增加一个菜单到窗体的控件模板中:

     

    <Style TargetType="{x:Type Window}">

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

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

                <Setter Property="Template">

                    <Setter.Value>

                        <ControlTemplate TargetType="{x:Type Window}">

                            <Border

                                      Background="{TemplateBinding Background}"

                                      BorderBrush="{TemplateBinding BorderBrush}"

                                      BorderThickness="{TemplateBinding BorderThickness}">

                                <DockPanel>

                                    <!--这里利用一个Menu只是为了说明的目的,你可以放入任何visuals元素来组成Window的外观-->

                                    <Menu DockPanel.Dock="Top"/>

                                    <AdornerDecorator>

                                        <ContentPresenter/>

                                    </AdornerDecorator>

                                </DockPanel>

                            </Border>

                        </ControlTemplate>

                    </Setter.Value>

                </Setter>

            </Style>

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/51d085e8-e363-43f0-8583-e93a680dc1d8/

     

    2009年7月9日 4:25
  •  

    6. 数据服务  [回到顶端]

     

    6.1 TemplateBindingBinding有什么区别?  [回到顶端]

      

    TemplateBindingBinding的一个轻量级版本,它失去了成熟版本Binding的很多功能,比如继承内容引用(inheritence context referencing),RelativeSource引用,还有通过IValueConverter/TypeConverter机制的动态类型转换。它仅支持由模板产生的FrameworkElements,它的数据源引用会指向模板中的父级元素。TemplateBinding最主要的用途是内置在模板中绑定模板化元素的属性,在这种情况下,比起成熟Binding效率要高得多。

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/edec50f6-d1c7-4891-a814-a9c6f154baf9/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/0cb2303e-3152-4ff5-a17c-745f9f06f123/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/410dd6d2-07ff-417b-9f51-0c0f7bb0481b/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/c9129784-2f1e-4a94-b4f5-2def3230d46d/

     

    2009年7月9日 4:26
  •  

    6.2 CollectionView是什么?  [回到顶端]

     

    以下是MSDN文档的说明:

    CollectionView数据源提供一个视图,它提供了分组,排序和过滤的功能,并且可以指向当前的数据项。

     

    CollectionView位于UI/Viewdata/DataModel之间,提供了一个视图能够理解并能触发改变通知的单个接口或是统一的契约.

      

    UI的角度来看,它仅了解INotifyCollectionChangedINotifyPropertyChanged接口,当数据源执行INotifyCollectionChanged接口时,与其对应的CollectionView的执行会注册CollectionChanged事件,并且使之应用于CollectionChangedPropertyChanged事件(注意CollectionView本身已执行INotifyCollectionChangedsINotifyPropertyChanged接口),当下属的数据源执行IBindingList接品比如ADO.NETDataView的时候),其下属的CollectionView(在这种情况是BindingListCollectionView)执行会订阅ListChanged事件,并使之执行CollectionChangedPropertyChanged事件。

     

    当你设计数据驱动的WPF程序时,你应该时常想到View/ViewModel/Model模式,因为WPF围绕着这些模式内建了数据绑定机制,举例来说,如果你用一个ListBox控件绑定一个IList集合时,IList集合将会是DataModel模式,中间隐式的CollectionView(又称为ListCollectionView)将会是ViewModel模式,ListBox本身会是View模式。如果你想了解更多关于V-VM-M模式的内容,请参照以下John Gossman的博客:

      

    http://blogs.msdn.com/johngossman/archive/2005/10/08/478683.aspx

    http://blogs.msdn.com/johngossman/archive/2006/03/07/545371.aspx

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/453c72e1-b254-4997-bdd7-6429adb5f204/

     

    2009年7月9日 4:28
  •  

    6.3 多线程中怎样实现数据绑定?  [回到顶端]

     

    WPF提供了在线程边界(thread boundaries)传送属性和改变通知机制的功能,但是集合改变通知机制并不传送。原因就是如果你在线程间传送集合通知机制,不管是同步执行还是异步执行,这都会在你调用分配线程与执行之间产生时间间隔。在这个时间间隔中,分配线程有可能会访问已经改变的数据(注意在这个间隔中,有可能存在工作项(work items)在你的工作项前发布或传送,并且这些工作项有可能会改变绑定的数据集合)。由于数据已经改变,分配线程可能会得到错误的数据,所以程序最终有可能崩溃。

     

    在当前的WPF版本中,最好执行多线程数据绑定的方法就是集中所有与分配线程相关的数据源的操作,这个方法已经在Beatriz Costa的博客中有清晰的讲解:

     

    http://www.beacosta.com/blog/?p=34

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/e55530dc-a358-4c6b-81b6-25a11c173a22/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/a5347c63-cbcd-4041-891e-ca8018db8ad8/

     

    2009年7月9日 4:29
  •  

    7. 图形 (2D/3D 图形, 动画 & 音频)  [回到顶端]

     

    7.1 怎样使用RenderTargetBitmap?  [回到顶端]

     

    RenderTargetBitmap把视觉树中的一部分光栅化以位图的形式保存,你可以利用下面的这个类以位图的形式呈现视觉元素,当然得考虑系统DPI设置及视觉元素的转换。(感谢Adam Smith在反转换这方面的建议)

     

     public class VisualUtility

     {

            public static BitmapSource CreateBitmapFromVisual(Double width,

                    Double height,

                    Visual visualToRender,

                    Boolean undoTransformation)

            {

                if (visualToRender == null)

                {

                    return null;

                }

                // The PixelsPerInch()方法用于读取屏幕上DPI的设置

                //如果你想以特定的分辨率创建一个位图,你可以直接把某一dpiXdpiY传参给RenderTargetBitmap构造器。

                RenderTargetBitmap bmp = new RenderTargetBitmap((Int32)Math.Ceiling(width),

                                                                (Int32)Math.Ceiling(height),

                                                                (Double)DeviceHelper.PixelsPerInch(Orientation.Horizontal),

                                                                (Double)DeviceHelper.PixelsPerInch(Orientation.Vertical),

                                                                PixelFormats.Pbgra32);

                //如果我们想反转换,我们可以使用VisualBrush

                if (undoTransformation)

                {

                    DrawingVisual dv = new DrawingVisual();

                    using (DrawingContext dc = dv.RenderOpen())

                    {

                        VisualBrush vb = new VisualBrush(visualToRender);

                        dc.DrawRectangle(vb, null, new Rect(new Point(), new Size(width, height)));

                    }

                    bmp.Render(dv);

                }

                else

                {

                    bmp.Render(visualToRender);

                }

                return bmp;

            }

        }

     

        internal class DeviceHelper

        {

            public static Int32 PixelsPerInch(Orientation orientation)

            {

                Int32 capIndex = (orientation == Orientation.Horizontal) ? 0x58 : 90;

                using (DCSafeHandle handle = UnsafeNativeMethods.CreateDC("DISPLAY"))

                {

                    return (handle.IsInvalid ? 0x60 : UnsafeNativeMethods.GetDeviceCaps(handle, capIndex));

                }

            }

        }

     

        internal sealed class DCSafeHandle : SafeHandleZeroOrMinusOneIsInvalid

        {

            private DCSafeHandle() : base(true) { }

            protected override Boolean ReleaseHandle()

            {

                return UnsafeNativeMethods.DeleteDC(base.handle);

            }

        }

     

       [SuppressUnmanagedCodeSecurity]

        internal static class UnsafeNativeMethods

        {

            [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]

            public static extern Boolean DeleteDC(IntPtr hDC);

            [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]

            public static extern Int32 GetDeviceCaps(DCSafeHandle hDC, Int32 nIndex);

            [DllImport("gdi32.dll", EntryPoint = "CreateDC", CharSet = CharSet.Auto)]

            public static extern DCSafeHandle IntCreateDC(String lpszDriver,

                String lpszDeviceName, String lpszOutput, IntPtr devMode);

            public static DCSafeHandle CreateDC(String lpszDriver)

            {

                return UnsafeNativeMethods.IntCreateDC(lpszDriver, null, null, IntPtr.Zero);

            }

      }

     

    之所以你需要反转换是因为如果你需要光栅化成RenderTargetBitmap的视觉目标元素已经是转换过的话(比如旋转,比例缩放,或是平移之类),这些效果会对最终产生位图有影响,这可能不是你想要的结果。“反转换”参数不仅使你能够实现反转换,而且可以得到原始的未经过转换的视觉元素呈现。

     

    在利用RenderTargetBitmap时的一些限制你也应该清楚:首先,RenderTargetBitmap不会利用硬件加速,位图是完全在内存中产生的,并且整个过程也是在UI线程中实现的。其次,字体的显示会显示锯齿效果,而不是清晰的呈现线条样的效果。

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/101a3c48-af17-4a7c-9e2a-624e14d6602e/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/fb84fbcb-fe36-45a7-bc1a-0dd0d0bb9fdb/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/984da366-33d3-4fd3-b4bd-4782971785f8/

     

    2009年7月9日 4:30
  •    

    7.2 怎样使WPF窗体呈现动画效果?  [回到顶端]

     

    Win32的角度来说,WPF的窗体本质上是一个HWND窗体,并且GDI/GDI+的呈现模型也没有专为动画效果进行优化,然而窗体暴露了两个改变大小的依赖属性:高度和宽度,这两个属性支持动画。但是结果并不如你所愿,因为你可能会得到一个闪烁、生硬的动画效果。

     

    为了产生一个更好的动画效果, 你可以设置窗体的AllowsTransparency属性为True并把WindowStyle属性设为WindowStyle.None来设置分层窗体。把内置在窗体中的内容执行动画效果。以这种方式的话,你是把WPF内容而不是一个HWND容器。

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/e7b2038c-7544-438c-9960-f420655039b5/

     

    2009年7月9日 4:30
  •  

    7.3 当Viewport2DVisual3D.Camera使用动画时为什么是冻结的?  [回到顶端]

     

    当你动画化Viewport2DVisual3D对象的“照像机”属性时,你会得到一个“错误操作异常”:冻结的对象不能被冻结“。

     

    Freezable对象在有些情况下比如设置在样式中时会被冻结,有关Freezable对象,请参照以下MSDN的相关文档:

     

    http://msdn.microsoft.com/en-us/library/ms750509.aspx

     

    这里的原因就是WPF不会冻结Viewport2DVisual3D对象,WPF所做的就是制作一个“camera “冻结的副本并把它分配给对应的转换(underlying transformation),这些转换会在点击测试机制中取消Viewport2DVisual3D对象的点击性能,从而使其不能与用户交互。

     

    这里有一个问题就是包含表达式(不管是设置在数据绑定中还是在动态的资源引用)的冻结对象不能被冻结,因为如果你要绑定冻结对象的时候,你是打算改变它的。

     

    为了绕开这个限制,你可以在“照像机“属性上避免使用数据绑定。

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/f1c4545b-6822-4468-92e0-649a15d97648/

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/952f4538-a410-4ed1-83d4-6f8c36146747/

     

    2009年7月9日 4:31
  •  

    8. 图像   [回到顶端]

     

    8.1 在WPF中有方法可以合在像素级别上绘制图形吗?  [回到顶端]

     

    WPF提供了很多原始的呈现视觉效果的图形元素,比如ShapesVisualsDrawings,几何图形等。但是有些时候,你需要在像素的级别自己去绘出图形。在当前版本中最有用的方法就是使用:Imaging.CreateBitmapSourceFromMemorySection方法,下面是一个例子(由Mike Cook提供)

     

        class Example : Window

        {

            public Example()

            {

                SizeToContent = SizeToContent.WidthAndHeight;

                int pixelWidth = 20, pixelHeight = 20;

                uint numPixels = (uint)(pixelWidth * pixelHeight);

                uint numBytes = numPixels * 4;

                //创建一个新的文件匹配返回的位图

                IntPtr section = CreateFileMapping(INVALID_HANDLE_VALUE,

                                                   IntPtr.Zero,

                                                   PAGE_READWRITE,

                                                   0,

                                                   numBytes,

                                                   null);

                //我们决定运行都是正常的,并且把位图中的像素设置为红色

                unsafe

                {

                    int* vptr = (int*)MapViewOfFile(section,

                                                    FILE_MAP_ALL_ACCESS,

                                                    0,

                                                    0,

                                                    numBytes).ToPointer();

                    for (int i = 0; i < numPixels; i++)

                    {

                        vptr[i] = 0xFF0000;

                    }

                }

                int stride = (pixelWidth * PixelFormats.Bgr32.BitsPerPixel + 7) / 8;

                //利用从CreateFileMapping中得到来的memory section创建一个位图

                BitmapSource bitmapSource

                  = Imaging.CreateBitmapSourceFromMemorySection(section,

                                                                pixelWidth,

                                                                pixelHeight,

                                                                PixelFormats.Bgr32,

                                                                stride,

                                                                0);

                //把我们的BitmapSource设置为一个新图片的source,并显示这个图片

                Image image = new Image();

                image.Width = image.Height = 300;

                image.Source = bitmapSource;

                Content = image;

            }

            [STAThread]

            public static void Main()

            {

                Application app = new Application();

                app.Run(new Example());

            }

            [DllImport("kernel32.dll", SetLastError = true)]

            static extern IntPtr CreateFileMapping(IntPtr hFile,

                                                   IntPtr lpFileMappingAttributes,

                                                   uint flProtect,

                                                   uint dwMaximumSizeHigh,

                                                   uint dwMaximumSizeLow,

                                                   string lpName);

            [DllImport("kernel32.dll", SetLastError = true)]

            static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject,

                                               uint dwDesiredAccess,

                                               uint dwFileOffsetHigh,

                                               uint dwFileOffsetLow,

                                               uint dwNumberOfBytesToMap);

            // Windows constants

            uint FILE_MAP_ALL_ACCESS = 0xF001F;

            uint PAGE_READWRITE = 0x04;

            IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

    }

     

    接下来的NET Framework 3.5 SP1版本会包含一个新的WriteableBitmapWriteableBitmap能提供一个更快的执行方式,且与UI及时同步更新和避免产生闪烁,下面的链接提供了在SP1版本发布之前 的解决方案:

     

    相关链接:

    http://social.msdn.microsoft.com/forums/en-US/wpf/thread/a8d86cd0-10cc-4349-aa9c-e62d7d066508/

     

    2009年7月9日 4:31