WPF FAQ

I am excited to announce the availability of the following FAQ paper which lists some of the questions frequently asked by the WPF forum members. This paper has covered many parts of WPF from the application model to base services to graphics, animation etc. Although it’s far from complete, but this is the first attempt our forum support team has been trying to make to help development community members find the answers to their questions much easier and fast, and it’s always the top priority for our team to make the MSDN forum a good place for developers to ask questions, talk about technologies etc. With time goes on, this thread will be enhanced with more FAQs and content, so we really welcome and appreciate any feedback or suggestion on how to improve it.


On behalf of the whole forum support team, I want to thank
Ian Griffiths from the community for his uncompromising technical review on this paper. And I also want to thank Dwayne Need from the WPF team for his expert tips on how to explain the nitty-gritty aspects of WPF. His final technical review is gorgeous and definitive. And finally I want to thank all the WPF forum members who actively participate in this forum and help others.

 



1. Application Model

1.1 Is WPF resolution independent?
1.2 Should we manually cleanup the resources used by WPF bitmaps or Image controls?
1.3 Is it possible to change the Z-order of hosted Windows Forms control in WPF?
1.4 How to host a WPF/XBAP application in ASP.NET pages?
1.5 How to use Windows Forms control (e.g. Crystal Report Viewer) in a WPF application?
1.6 Is it possible to subclass from XAML generated class in WPF?

2. Base Services (Threading, Keyboard/Mouse handling & Dependency Properties)

2.1 Where is Application.DoEvents in WPF?
2.2 How to access WPF controls from another thread?

 

3. Controls

3.1 How to expand all nodes (TreeViewItems) of TreeView control?
3.2 How to do lasso (drag & select) selection in ListBox/ListView?
3.3 How to implement RadioButtonList type of control in WPF?
3.4 How to keep only one Expander control opened in a group of Expander controls?
3.5 How to automatically check TreeView children nodes when parent is checked?
3.6 What is ContentPresenter?

4. Text & Documents

4.1 How to find and replace text in RichTextBox?

5. Styles

5.1 When styling Button control, why does it have different theming behaviors under Windows XP and Windows Vista?
5.2 How to re-style or re-template a Popup control?
5.3 How to customize a Window in WPF?

6. Data Service

6.1 What's the difference between TemplateBinding and Binding?
6.2 What is CollectionView?
6.3 How to enable multi-threaded data binding in WPF?

7. Visuals (2D/3D Graphics, Animation & Media)

7.1 How to use RenderTargetBitmap?
7.2 How to animate the size of a Window in WPF?
7.3 Why Viewport2DVisual3D.Camera is frozen when animating it?

 

8. Imaging

8.1 Is there any efficient method in WPF to draw graphics at pixel level?

 

 

 

 

 

Edited by Marco Zhou on 2008年6月26日 11:29:49
Note: correct stuff
Edited by Marco Zhou on 2008年6月26日 11:34:08
Note: correct stuff
Edited by Marco Zhou on 2008年6月26日 11:38:46
Note: correct stuff
Edited by Marco Zhou on 2008年6月26日 11:50:09
Note: correct stuff
Edited by Marco Zhou on 2008年6月26日 11:52:29
Note: correct stuff
Edited by Marco Zhou on 2008年6月26日 16:30:07
Note: add preface
Edited by Marco Zhou on 2008年6月26日 16:34:21
Note: correct stuff
Edited by Marco Zhou on 2008年6月26日 17:01:40
Note: correct font
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 11:18:15
      

    1.  Application Model

    1.1 Is WPF resolution independent?

     

    This is an occasionally confusing aspect of WPF. Thank Charles Petzold for his concise and definitive answer:

     

    The Windows user is ultimately responsible for setting an assumed resolution of the video display. This is done in the Display Properties applet. Commonly this assumed resolution is 96 or 120 DPI but it could be set higher. This has been the case for several past versions of Windows and it remains unchanged in Vista.

    The assumed resolution is independent of the actual resolution of the monitor. The user can try to set the assumed resolution equal to the actual resolution, but there is little reason for it. But again, nothing has changed with Vista.

    WPF programming involves device-independent units of 1/96th inch. If the assumed resolution of the video display is 96 DPI, then WPF DIUs correspond directly with pixels. If the assumed resolution of the video display is 120 DPI, then WPF DIUs are equal to 1-1/3 pixels.

    Functionally, there is no real change. For decades, Windows programmers have either been obtaining the assumed resolution of the video display or using mapping modes based on this assumed resolution. The whole process is somewhat more convenient in WPF than in previous Windows APIs, but it's functionally the same.

    The WPF DIU is only something the programmer sees. It does not affect users at all. Users are not required to make any adjustments to accommodate the WPF DIU.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3057227&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2566603&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1857144&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2804844&SiteID=1
    Edited by Marco Zhou on 2008年6月26日 11:47:06
    Note: correct stuff
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 12:03:04

    1.2 Should we manually cleanup the resources used by WPF bitmaps or Image controls?

    Unlike Windows Forms, WPF has implemented a unified model for managing resources such as brushes, textures/bitmaps, video/audio etc. Let's take Image control for example, if the Image control is disconnected from the visual tree, its underlying unmanaged resources such as bitmaps will be released by the underlying MIL (Media Integration Layer), and if there are no outstanding managed references to it, the managed resources associated with it will be eligible for garbage collection. But if the unmanaged resources are explicitly created by your code, you are responsible for disposing them by implementing the dispose pattern.

    If you do find a memory leak problem or you suspect some of the objects are leaking in your application, you could use windbg or CLR profiler to track down those types of problems.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3086068&SiteID=1

    1.3 Is it possible to change the Z-order of hosted Windows Forms control in WPF?

    Windows Forms control when hosted inside a WPF window will always be on top of other WPF visuals rendered into its containing Window. This is one of the limitations you should take into consideration when using HwndHost/WindowsFormsHost to host Win32/Windows Forms control.

    For more background information on this, you can refer the following MSDN article:
    http://msdn2.microsoft.com/en-us/library/aa970688.aspx

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3087729&SiteID=1

    Edited by Marco Zhou on 2008年6月26日 13:30:52
    Note: correct stuff
    Edited by Marco Zhou on 2008年6月26日 13:42:28
    Note: correct stuff
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 12:28:24
    1.4 How to host a WPF/XBAP application in ASP.NET pages?

    Currently integration and interoperation between XBAP and ASP.NET is quite limited. Fortunately you can use HTML iframe element to host XBAP application, and use either cookies or query strings to communicate between ASP.NET and XBAP. Furthermore, if you really need a richer integration or interoperation features, you could try Silverlight (a subset of WPF) which is designed and optimized for web scenario.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2870386&SiteID=1

    1.5 How to use Windows Forms control (e.g. Crystal Report Viewer) in a WPF application?

     

    <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>

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2909472&SiteID=1

    1.6 Is it possible to subclass from XAML generated class in WPF?

     

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2829770&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2710858&SiteID=1

    The current version of WPF doesn't support subclassing from XAML generated class if the subclass also uses XAML. In order to let the base class inheritable by XAML generated subclass, you need to write the base class completely in code.

    We can use WindowsFormsHost element to host a Windows Forms control in WPF. Here is an example which shows how to host a CrystalReportViewer

    Edited by Marco Zhou on 2008年6月26日 15:07:52
    Note: correct stuff
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 12:55:53

    2. Base Services (Threading, Keyboard/Mouse handling & Dependency Properties)

    2.1 Where is Application.DoEvents in WPF?

    Unlike Windows Forms, WPF doesn't directly provide an Application.DoEvents() equivalent to allow developers to start nesting message pumping. When writing Windows application, you'd better try to avoid using nested message pumping, because nested message pumping could cause some subtle problems such as code reentrancy, and what's more, WPF will disable nested message pumping when doing layout, visual tree traversal among other things.

    But there would be some rare situations in which nested message pumping could be useful, such as unit testing, the following helper class could provide the Application.DoEvents like feature in WPF:

    /// <summary>
    ///
    Encapsulates a WPF dispatcher with added functionalities.
    /// </summary>
    public class DispatcherHelper
    {
        private static DispatcherOperationCallback exitFrameCallback = new
             DispatcherOperationCallback(ExitFrame);

        /// <summary>
        /// Processes all UI messages currently in the message queue.
        /// </summary>
        public static void DoEvents()
        {
            // Create new nested message pump.
            DispatcherFrame nestedFrame = new DispatcherFrame();

            // Dispatch a callback to the current message queue, when getting called,
            // this callback will end the nested message loop.
            // note that the priority of this callback should be lower than that of UI event messages.
            DispatcherOperation exitOperation = Dispatcher.CurrentDispatcher.BeginInvoke(
                DispatcherPriority.Background, exitFrameCallback, nestedFrame);

            // pump the nested message loop, the nested message loop will immediately
            // process the messages left inside the message queue.
            Dispatcher.PushFrame(nestedFrame);

            // If the "exitFrame" callback is not finished, abort it.
            if (exitOperation.Status != DispatcherOperationStatus.Completed)
            {
                exitOperation.Abort();
            }
        }

        private static Object ExitFrame(Object state)
        {
            DispatcherFrame frame = state as DispatcherFrame;

            // Exit the nested message loop.
            frame.Continue = false;
            return null;
        }
    }

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2034805&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=602081&SiteID=1

    Edited by Marco Zhou on 2008年6月26日 13:01:01
    Note: correct stuff
    Edited by Marco Zhou on 2008年6月26日 14:19:30
    Note: correct stuff
    Edited by Marco Zhou on 2008年6月26日 14:22:22
    Note: correct stuff
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 13:21:27

    2.2 How to access WPF controls from another thread?

    Like many UI frameworks such as Windows Forms, WPF also imposes a single threading model, which means that you can only access a specified DispatcherObject derivative from the thread which creates it. In Windows Forms, each Control will implement ISynchronizeInvoke interface, this interface exposes a set of methods such as Invoke and BeginInvoke to impose a common thread synchronization contract which we could use to access a control from another thread. In WPF, we also have such type of thing, but those operations are wrapped up in a class called Dispatcher, Dispatcher is WPF way of enabling this type of thread synchronization model.

    The following is an example of how to modify the TextBox.Text property when the caller is in a different thread:

    // Resets textbox text from another thread
    textBox.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
    {
        textBox.Text = "New text";
    }));

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3024118&SiteID=1

  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:03:33

    3. Controls

    3.1 How to expand all nodes (TreeViewItems) of TreeView control?

    Unlike Windows Forms, the current version of WPF doesn't provide a straightforward way to expand all nodes of TreeView control with a single line of code. Generally speaking, there are two ways to implement this feature. The first method is to leverage WPF's Styling capability as the following XAMLPad ready example shows:

    <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>

    Sometimes, you need a programmatic way of doing this, and then you could use the following C# helper method to expand all nodes:

    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)
                {
                    // Expand the current item.
                    currentContainer.IsExpanded = true;
                    if (currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
                    {
                        // If the sub containers of current item is not ready, we need to wait until
                        // they are generated.
                        currentContainer.ItemContainerGenerator.StatusChanged += delegate
                        {
                            ExpandSubContainers(currentContainer);
                        };
                    }
                    else
                    {
                        // If the sub containers of current item is ready, we can directly go to the next
                        // iteration to expand them.
                        ExpandSubContainers(currentContainer);
                    }
                }
            }
        }
    }

    The key to this technique shown above is to make sure that the container for the current TreeViewItem has been generated, so you can safely expand it to show all its sub items. That's why you need to make a recursive call when the status of current TreeViewItem's ItemContainerGenerator is GeneratorStatus.ContainersGenerated.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2014662&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3180239&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1130017&SiteID=1

    Edited by Marco Zhou on 2008年6月26日 16:22:06
    Note: correct stuff
    Edited by Marco Zhou on 2008年6月26日 18:40:26
    Note: correct stuff
    Edited by Marco Zhou on 2008年6月26日 18:42:43
    Note: correct stuff
    Edited by Marco Zhou on 2008年6月26日 18:45:10
    Note: correct stuff
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:05:39

    3.2 How to do lasso (drag & select) selection in ListBox/ListView?

    Lasso selection is used quite often in most graphical editing programs, and it's a convenient way to select multiple on-screen objects. The following shows how to enable this type of selection to the WPF ListBox/ListView controls:

    <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
    {
        // This field is used to tell if ListBox is in mouse selection mode.
        private Boolean inMouseSelectionMode = false;

        // This field is used to keep track of the current mouse selected items.
        private List<ListBoxItem> selectedItems = new List<ListBoxItem>();

        public Window1()
        {
            InitializeComponent();
        }

        private void ListBoxItem_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            // If the mouse is up, turn off "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;

                // Highlight the currently mouse-overed item.
                mouseOverItem.SetValue(TextElement.ForegroundProperty, SystemColors.HighlightTextBrush);

                if (!selectedItems.Contains(mouseOverItem))
                {
                    selectedItems.Add(mouseOverItem);
                }
            }
        }

        private void ListBoxItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // When the mouse is down, we need to clear all previously mouse selected items.
            listBox.SelectedIndex = -1;
            inMouseSelectionMode = true;
            foreach (ListBoxItem item in selectedItems)
            {
                item.ClearValue(ListBoxItem.BackgroundProperty);
                item.ClearValue(TextElement.ForegroundProperty);
            }

            selectedItems.Clear();
        }
    }

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2443181&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2427163&SiteID=1

  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:07:33

    3.3 How to implement RadioButtonList type of control in WPF?

    WPF doesn't provide RadioButtonList control like ASP.NET does. Fortunately with WPF's powerful styling and templating capability, we could implement this type of control purely in XAML. Here is a XAMLPad ready example:

    <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>

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3023610&SiteID=1

  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:09:32

    3.4 How to keep only one Expander control opened in a group of Expander controls?

    ExpanderList is a type of feature that is quite useful if you’re familiar with the left side bar of Outlook. You can place Expander controls in a ListBox, and bind the Expander.IsExpanded property to ListBoxItem.IsSelected property, leveraging ListBox's single selection nature, so we can keep only one Expander expanded, and the following is a XAMLPad ready example:

    <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>

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3061227&SiteID=1

     

    Edited by Marco Zhou on 2008年6月26日 14:16:48
    Note: correct stuff
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:25:45

    3.5 How to automatically check TreeView children nodes when parent is checked?

    We can use data binding to do this. We can create a class which contains a boolean property which will be bound to CheckedBox's IsChecked property and a collection property which will be bound to TreeViewItem's children nodes. When a TreeViewItem's CheckBox state is changed, its children nodes check state will be set by child node's boolean property. Here is an example:

    <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
    {
        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; private set; }
    }

    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;
            }
        }
    }

    public class Container : INotifyPropertyChanged
    {
        bool isChecked;
        public bool IsChecked
        {
            get { return this.isChecked; }
            set
            {
                this.isChecked = value;
                RaisePropertyChanged("IsChecked");
            }
        }
        public Node Node { get; set; }
    }

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3121689&SiteID=1

  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:30:16

    3.6 What is ContentPresenter?

    ContentPresenter is WPF way to implement content model for elements and controls. ContentPresenter by definition is used to present content, those contents could be text, image, graphical shapes or even CLR objects, XML, and data grabbed from database. To correctly present contents, WPF use what called DataTemplate. ContentPresenter has built a set of default data templates to present UIElements, strings, XML data, and documents etc. You could override ContentPresenter's default template negotiation/determination process by specifying your own customized DataTemplate or DataTemplateSelector or you could use the default templates available, for instance, to present text you can simply write something like the following:

    <ContentPresenter Content="WPF"/>

    To present UIElements, you can simply write this:

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

    To present XmlNodes, you can do this:

    <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>

    To present documents, you can do this:

    <ContentPresenter>
      <
    ContentPresenter.Content>
        <
    FlowDocument>
          <
    Paragraph>WPF</Paragraph>
        </
    FlowDocument>
      </
    ContentPresenter.Content>
    </
    ContentPresenter>

    The reason ContentPresenter knows how to present them is because ContentPresenter has built a set of default templates and the template negotiation process will choose to right template to present its corresponding "data".

    You could override ContentPresenter's template negotiation process by specifying its ContentTemplate or ContentTemplateSelector property, the following example shows how to present a CLR type object and display its Namespace property value:

    <ContentPresenter>
      <
    ContentPresenter.Content>
        <
    x:Type TypeName="ContentPresenter"/>
      </
    ContentPresenter.Content>
      <
    ContentPresenter.ContentTemplate>
        <
    DataTemplate>
          <
    TextBlock Text="{Binding Namespace}"/>
        </
    DataTemplate>
      </
    ContentPresenter.ContentTemplate>
    </
    ContentPresenter>

    With the help of ContentPresenter, you can display any type of Content/Data in the UI and usually ContentPresenter will be placed inside ControlTemplate to display the "content" portion of the element tree generated by ControlTemplate.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2574519&SiteID=1

  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:32:56

    4 Text & Documents

    4.1 How to find and replace text in RichTextBox?

    Unlike Windows Forms, the current version of WPF doesn't directly incorporate the search functionality into RichTextBox, fortunately the TextRange and TextPointer APIs provided by the framework can assist us to implement our own version of text search and replacement feature. Here is an example:

    /// <summary>
    ///
    This class represents the possible options for search operation.
    /// </summary>
    [Flags]
    public enum FindOptions
    {
        /// <summary>
        /// Perform case-insensitive non-word search.
        /// </summary>
        None = 0x00000000,
        /// <summary>
        /// Perform case-sensitive search.
        /// </summary>
        MatchCase = 0x00000001,
        /// <summary>
        /// Perform the search against whole word.
        /// </summary>
        MatchWholeWord = 0x00000002,
    }

    /// <summary>
    ///
    This class encapsulates the find and replace operations for<see cref="FlowDocument"/>.
    /// </summary>
    public sealed class FindAndReplaceManager
    {
        private FlowDocument inputDocument;
        private TextPointer currentPosition;

        /// <summary>
        /// Initializes a new instance of the<see cref="FindReplaceManager"/>
        /// class given the specified<see cref="FlowDocument"/> instance.
        /// </summary>
        /// <param name="inputDocument">the input document</param>
        public FindAndReplaceManager(FlowDocument inputDocument)
        {
            if (inputDocument == null)
            {
                throw new ArgumentNullException("documentToFind");
            }

            this.inputDocument = inputDocument;
            this.currentPosition = inputDocument.ContentStart;
        }

        /// <summary>
        /// Gets and sets the offset position for the<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>
        /// Find next match of the input string.
        /// </summary>
        /// <param name="input">The string to search for a match.</param>
        /// <param name="findOptions">the search options</param>
        /// <returns>The<see cref="TextRange"/> instance representing the input string.</returns>
        /// <remarks>
        /// This method will advance the<see cref="CurrentPosition"/> to next context position.
        /// </remarks>
        public TextRange FindNext(String input, FindOptions findOptions)
        {
            TextRange textRange = GetTextRangeFromPosition(ref currentPosition, input, findOptions);
            return textRange;
        }

        /// <summary>
        /// Within a specified input string, replaces the input string that
        /// match a regular expression pattern with a specified replacement string.
        /// </summary>
        /// <param name="input">The string to search for a match.</param>
        /// <param name="replacement">The replacement string.</param>
        /// <param name="findOptions"> the search options</param>
        /// <returns>The<see cref="TextRange"/> instance representing the replacement string.</returns>
        /// <remarks>
        /// This method will advance the<see cref="CurrentPosition"/> to next context position.
        /// </remarks>
        public TextRange Replace(String input, String replacement, FindOptions findOptions)
        {
            TextRange textRange = FindNext(input, findOptions);
            if (textRange != null)
            {
                textRange.Text = replacement;
            }

            return textRange;
        }

        /// <summary>
        /// Within a specified input string, replaces all the input strings that
        /// match a specified criteria with a specified replacement string.
        /// </summary>
        /// <param name="input">The string to search for a match.</param>
        /// <param name="replacement">The replacement string.</param>
        /// <param name="findOptions"> the search options</param>
        /// <param name="action">the action performed for each match of the input string.</param>
        /// <returns>The number of times the replacement can occur.</returns>
        /// <remarks>
        /// This method will advance the<see cref="CurrentPosition"/> to last context position.
        /// </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;
        }

        /// <summary>
        /// Finds the corresponding<see cref="TextRange"/> instance
        /// representing the input string given a specified text pointer position.
        /// </summary>
        /// <param name="position">the current text position</param>
        /// <param name="textToFind">input text</param>
        /// <param name="findOptions">the search option</param>
        /// <returns>
        /// An<see cref="TextRange"/> instance represeneting the matching
        /// string withing the text container.
        /// </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)) // Test if the "textRange" represents a word.
                            {
                                // If a WholeWord match is found, directly terminate the loop.
                                position = position.GetPositionAtOffset(input.Length);
                                break;
                            }
                            else
                            {
                                // If a WholeWord match is not found, go to next recursion to find it.
                                position = position.GetPositionAtOffset(input.Length);
                                return GetTextRangeFromPosition(ref position, input, findOptions);
                            }
                        }
                        else
                        {
                            // If a none-WholeWord match is found, directly terminate the loop.
                            position = position.GetPositionAtOffset(input.Length);
                            break;
                        }
                    }
                    else
                    {
                        // If a match is not found, go over to the next context position after the "textRun".
                        position = position.GetPositionAtOffset(textRun.Length);
                    }
                }
                else
                {
                    //If the current position doesn't represent a text context position, go to the next context position.
                    // This can effectively ignore the formatting or embed element symbols.
                    position = position.GetNextContextPosition(LogicalDirection.Forward);
                }
            }

            return textRange;
        }

        /// <summary>
        /// determines if the specified character is a valid word character.
        /// here only underscores, letters, and digits are considered to be valid.
        /// </summary>
        /// <param name="character">character specified</param>
        /// <returns>Boolean value didicates if the specified character is a valid word character</returns>
        private Boolean IsWordChar(Char character)
        {
            return Char.IsLetterOrDigit(character) || character == '_';
        }

        /// <summary>
        /// Tests if the string within the specified<see cref="TextRange"/>instance is a word.
        /// </summary>
        /// <param name="textRange"><see cref="TextRange"/>instance to test</param>
        /// <returns>test result</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;
        }
    }

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2403758&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=923088&SiteID=1

  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:40:21

    5. Styles

    5.1 When styling Button control, why does it have different theming behaviors under Windows XP and Windows Vista?

    When you style the Button control in WPF as follows:

    <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>

    You will see that the IsMouseOver trigger doesn't appear to work correctly when running under Windows Vista, but it does under Windows XP.

    The reason here is that the ButtonChrome implementation in PresentationFramwork.Luna.dll which is used under Windows XP, and the ButtonChrome implementation in PresentationFramwork.Aero.dll which is used under Windows Vista have different "interpretation" of the property called RenderMouseOver. Under Windows XP, it will simply draw a highlight border by overriding the ButtonChrome.OnRender() method, but under Windows Vista, it will additionally draw a highlight background to simulate Vista Aero theme which will obscure the background you set using style trigger.

    To workaround this issue, you’d better replace the Button’s default ControlTemplate with your own implementation.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2074626&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3150244&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2671127&SiteID=1

  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:43:17

    5.2 How to re-style or re-template a Popup control?

    Popup is derived from FrameworkElement, so it supports framework level services such as Styling, So you could directly style it through FrameworkElement.Style property as usual FrameworkElement derivatives do. But Popup control doesn't support control templating. In order to make Popup support templating, you could try placing a Control inside Popup, and redefine its ControlTemplate as follows:

    <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>

    Another option you could choose is to place a ContentPresenter inside the Popup control to support data templating.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3019128&SiteID=1

  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:52:05

    5.3 How to customize a Window in WPF?

    As we know that a WPF control can be re-templated to change its appearance, but in fact, a WPF window can also be re-templated to change its appearance. Here is a small example which shows how to add a menu to a window's control template:

    <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>
                <!--
    Using Menu here is only for demonstration purpose,
                you could add any visuals which will make up the appearance of Window
    -->
                <
    Menu DockPanel.Dock="Top"/>
                <
    AdornerDecorator>
                  <
    ContentPresenter/>
                </
    AdornerDecorator>
              </
    DockPanel>
            </
    Border>
          </
    ControlTemplate>
        </
    Setter.Value>
      </
    Setter>
    </
    Style>

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3086289&SiteID=1

  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:55:32

    6. Data Service

    6.1 What's the difference between TemplateBinding and Binding?

    TemplateBinding is a lightweight version of Binding, it misses many capabilities of full blown Binding, such as inheritence context referencing, relative source referencing, dynamic type conversion through default IValueConverter/TypeConverter mechanism among many other things. It only supports binding on template generated FrameworkElements, and its data source referencing point will always point to its valid templated parent.

    The primary usage scenario for TemplateBinding is placed inside template to bind to the properties of templated control. In this situation, it will be more efficient than full-blown Binding.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2900036&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2350412&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2568742&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3232062&SiteID=1

    6.2 What is CollectionView?

    As the MSDN documentation says:

    CollectionView represents a view of a data collection. Views allow for the functionalities of grouping, sorting, filtering, and the concept of a current record pointer.

    CollectionView sits between the UI/View and the data/DataModel, and provides a single interface or common contract that the View can understand and will subscribe to for change notification.

    From the UI or View's perspective, it only knows about INotifyCollectionChanged or INotifyPropertyChanged interface. When the data source collection has implemented the INotifyCollectionChanged interface, the underling CollectionView implementation will subscribe to its CollectionChanged event, and adapt it into its own CollectionChanged or PropertyChanged event (note that CollectionView itself also implements INotifyCollectionChanged and INotifyPropertyChanged interface. And when the underlying data source implements IBindingList such as ADO.NET's DataView, the underlying CollectionView implementation (in this case it will be BindingListCollectionView) will subscribe to its ListChanged event, and adapt it into its own CollectionChanged or PropertyChanged event.

    When designing Data driven WPF application, you might need to always think about the View/ViewModel/Model pattern, because WPF has built its data binding mechanism around this pattern. For instance, if you have a listBox which is data bound to an IList collection, the IList collection will be the DataModel, the implicit CollectionView aka ListCollectionView will be the ViewModel, and ListBox will be the View itself. For more information on the V-VM-M pattern, please refer to the following John Gossman's blog posts:

    http://blogs.msdn.com/johngossman/archive/2005/10/08/478683.aspx
    http://blogs.msdn.com/johngossman/archive/2006/03/07/545371.aspx

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3332042&SiteID=1

    6.3 How to enable multi-threaded data binding in WPF?

    WPF does propagate the property changed notification across thread boundaries, but collection changed notification doesn’t get propagated , the reason is that if you marshal or propagate the collection changed notification across thread boundaries,  no matter whether you marshal the collection changed notification either synchronously (Dispatcher.Invoke()) or asynchronously (Dispatcher.BeginInvoke()), there will be an interval between the time you marshal the call to the dispatcher thread, and the time the dispatcher thread is available to process the call. During this interval, the dispatcher thread would probably access the already changed source collection (note that at this interval, there would be some work items posted or sent before your work item, and those work items would possibly change the data bound source collection) and since the collection has been changed, the dispatcher thread will get the wrong value, and possibly crash eventually.

    So in current version of WPF, the best way to implement multi-threaded data binding is to marshal all the operations relating to collection modification to the dispatcher thread, this approach is clearly articulated in the following Beatriz Costa’s blog post:
    http://www.beacosta.com/blog/?p=34

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2375562&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2390436&SiteID=1

    Edited by Marco Zhou on 2008年6月26日 14:56:59
    Note: correct stuff
    Edited by Marco Zhou on 2008年6月26日 17:43:01
    Note: correct stuff
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 14:59:55

    7 Visuals (2D/3D Graphics, Animation & Media)

    7.1 How to use RenderTargetBitmap?

    RenderTargetBitmap rasterizes a portion of visual tree into a bitmap, here is a utility class you could use to render visuals into a bitmap taking into consideration of system DPI setting and visual transformation (Thanks Adam Smith for his tip on how to undo transformation):

    public class VisualUtility
    {
        public static BitmapSource CreateBitmapFromVisual(Double width,
                Double height,
                Visual visualToRender,
                Boolean undoTransformation)
        {
            if (visualToRender == null)
            {
                return null;
            }

            // The PixelsPerInch() helper method is used to read the screen DPI setting.
            // If you need to create a bitmap with a specified resolution, you could directly
            // pass the specified dpiX and dpiY values to RenderTargetBitmap constructor.
            RenderTargetBitmap bmp = new RenderTargetBitmap((Int32)Math.Ceiling(width),
                                                            (Int32)Math.Ceiling(height),
                                                            (Double)DeviceHelper.PixelsPerInch(Orientation.Horizontal),
                                                            (Double)DeviceHelper.PixelsPerInch(Orientation.Vertical),
                                                            PixelFormats.Pbgra32);

            // If we want to undo the transform, we could use VisualBrush trick.
            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);
        }
    }

    The reason you need to undo transformation is that if the target Visual which you need to be rasterized into RenderTargetBitmap is transformed( for instance  rotated, scaled, translated  in some way), there transformations will be carried along and get applied to finally bitmap, which might not what you want. “undoTransformation” Parameter can give you the ability to undo the transformation, and get the original untransformed version of Visual bitmap output.

    There are some limitations you might need to know when using RenderTargetBitmap: first, RenderTargetBitmap will not be hardware accelerated, the bitmap is completely composed in the system memory, and this composition process is done in UI thread; second, font rendering will fall back to grayscale anti-aliasing instead of the clear-type rendering path.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2478706&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2355827&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=788263&SiteID=1

    Edited by Marco Zhou on 2008年6月26日 18:38:43
    Note: correct title
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 15:19:51

    7.2 How to animate the size of a Window in WPF?

    WPF's Window object is essentially a hwnd from Win32's perspective, and the rendering model of GDI/GDI+ is not designed or optimized for animation scenario, although Window exposes two sizing dependency property aka Height and Width which support animation, but the end result will be less desirable, because you might get tearing, flickering and jerky animation experience.

    In order to have a much better animation result, you could try using layered windows by setting Window.AllowsTransparency property to true and Window.WindowStyle property to WindowStyle.None, and animate the visual contents placed inside the window, because in this way, you are animating WPF content rather than its hwnd container.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3061501&SiteID=1

    7.3 Why Viewport2DVisual3D.Camera is frozen when animating it?

    When you animate the Camera property of Viewport2DVisual3D object, you will get caught by the following InvalidOperationException exception saying:
    "This Freezable cannot be frozen"

    Freezable objects will be frozen by WPF in some situation such as when used inside Style, for the explanation of Freezable, you can refer to the following MSDN documentation: http://msdn.microsoft.com/en-us/library/ms750509.aspx

    The reason here is that WPF doesn't freeze the Viewport2DVisual3D object, what WPF does is to take a frozen copy of the camera and assign it to the underlying transformation which will be used by hit testing mechanism to hit test against visuals inside Viewport2DVisual3D, this can make the Viewport2DVisual3D interactive.

    The problem here is that Freezable objects which contain expressions (either set up by data binding or dynamic resource referencing) cannot be frozen, because when you data bind against Freezable objects, you are meant to change them.

    To work around this limitation, you need to avoid using data binding on Camera property.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3292824&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2753906&SiteID=1

    Edited by Marco Zhou on 2008年6月26日 15:41:30
    Note: correct stuff
    Edited by Marco Zhou on 2008年6月26日 16:01:46
    Note: correct stuff
    Edited by Marco Zhou on 2008年6月26日 16:07:16
    Note: correct stuff
  • Marco Zhou MSFT, Moderator - Posted on 2008年6月26日 15:23:29
    8. Imaging


    8.1 Is there any efficient method in WPF to draw graphics at pixel level?

    WPF has provided a bunch of graphical primitives such as Shapes, Visuals, Drawings, Geometries etc to render visual contents, but sometimes, you need to manipulate graphical content at pixel level. The most efficient way in current version of WPF to draw things at pixel level is to use Imaging.CreateBitmapSourceFromMemorySection() method, here is an example (given by Mike Cook in the referenced thread):

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interop;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Runtime.InteropServices;

    namespace MemSectionBitmapExample
    {
        class Example : Window
        {
            public Example()
            {
                SizeToContent = SizeToContent.WidthAndHeight;
                int pixelWidth = 20, pixelHeight = 20;
                uint numPixels = (uint)(pixelWidth * pixelHeight);
                uint numBytes = numPixels * 4;

                // Create a new file mapping object to back our bitmap.
                IntPtr section = CreateFileMapping(INVALID_HANDLE_VALUE,
                                                   IntPtr.Zero,
                                                   PAGE_READWRITE,
                                                   0,
                                                   numBytes,
                                                   null);

                // Just so we can tell that everything is actually working, write
                // the color red to each pixel of the bitmap.
                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;

                // Create a bitmap source using the memory section we got
                // from CreateFileMapping
                BitmapSource bitmapSource
                  = Imaging.CreateBitmapSourceFromMemorySection(section,
                                                                pixelWidth,
                                                                pixelHeight,
                                                                PixelFormats.Bgr32,
                                                                stride,
                                                                0);

                // Set our BitmapSource as the source of a new Image so
                // that we can display it.
                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);
        }
    }

    The upcoming .NET Framework 3.5 SP1 will include a new WriteableBitmap which can provide fast pixel manipulation/update which is synchronized with UI update to avoid tearing, this will be the preferred solution when the SP1 is finally shipped.

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=368224&SiteID=1
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=214624&SiteID=1

    Edited by Marco Zhou on 2008年6月26日 15:26:28
    Note: correct stuff
    Marco Zhou said:

     

    2.2 How to access WPF controls from another thread?

    Like many UI frameworks such as Windows Forms, WPF also imposes a single threading model, which means that you can only access a specified DispatcherObject derivative from the thread which creates it. In Windows Forms, each Control will implement ISynchronizeInvoke interface, this interface exposes a set of methods such as Invoke and BeginInvoke to impose a common thread synchronization contract which we could use to access a control from another thread. In WPF, we also have such type of thing, but those operations are wrapped up in a class called Dispatcher, Dispatcher is WPF way of enabling this type of thread synchronization model.

    The following is an example of how to modify the TextBox.Text property when the caller is in a different thread:

    // Resets textbox text from another thread
    textBox.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
    {
        textBox.Text = "New text";
    }));

    Related threads:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3024118&SiteID=1

     

     

    Shouldn't the Dispatcher's BeginInvoke be used instead?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值