Microsoft WPF Recipes

Contents

Binding to DependencyProperties in your code-behind

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
 
 
        public bool IsFlagSet
        {
            get { return (bool)GetValue(_isFlagSetProperty); }
            set { SetValue(_isFlagSetProperty, value); }
        }
        public static readonly DependencyProperty _isFlagSetProperty =
            DependencyProperty.Register("IsFlagSet", typeof(bool), typeof(MainWindow), new FrameworkPropertyMetadata(false));
 
    }

<Window x:Class="CaffeineIT.Blog.XamlCollections.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <TextBlock>The flag value is:
        <TextBlock Text="{Binding Path=IsFlagSet}"/>
    </TextBlock>
</Window>

Most important aspect of this is that the DataContext must point to ‘Self’: {BindingĀ RelativeSource={RelativeSourceĀ Self}}.

source

Simple Binding to XML in a ListView

XMLFile1.xml

   1 <?xml version="1.0" encoding="utf-8" ?>
   2 <?xml version="1.0" encoding="utf-8" ?>
   3 <Parties>
   4   <Party Contact="Jim Shmekel"
   5          Qty="1"
   6          Amount="55.00" 
   7          Tot="55.00"/> 
   8   <Party Contact="Shmi Skywalker"
   9          Qty="1"
  10          Amount="20.00" 
  11          Tot="20.00"/>
  12   <Party Contact="Jon Ronson"
  13          Qty="1"
  14          Amount="23.00" 
  15          Tot="23.00"/>
  16 </Parties>

MainWindow.xaml

Note – you could bind directly to myParties – CollectionViewSource is put between myParties and the binding here, and gives extra functionality like sorting.

The key point here is in the ListView:

ItemsSource="{Binding Source={StaticResource myCollectionViewSource},XPath='Party',Mode=TwoWay}"

This is where the ListView is bound to the underlying XML data.

   1 <Window x:Class="MainWindow"
   2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4     xmlns:converters="clr-namespace:WpfApplication2"
   5     Title="MainWindow" Height="350" Width="525">
   6 
   7     <Window.Resources>
   8         <XmlDataProvider x:Key="myParties" XPath="Parties" Source="XMLFile1.xml" />
   9         <CollectionViewSource x:Key="myCollectionViewSource" Source="{StaticResource myParties}" />
  10         <converters:SumConverter x:Key="mySumConverter" />
  11     </Window.Resources>
  12 
  13     <StackPanel>
  14         <ListView x:Name="myListView" 
  15                   HorizontalAlignment="Stretch" 
  16                   ItemsSource="{Binding Source={StaticResource myCollectionViewSource},XPath='Party',Mode=TwoWay}">
  17             <ListView.View>
  18                 <GridView>
  19                     <GridViewColumn Width="100" DisplayMemberBinding="{Binding XPath='@Contact'}" Header="Contact"/>
  20                     <GridViewColumn DisplayMemberBinding="{Binding XPath='@Qty'}" Header="Q"/>
  21                     <GridViewColumn DisplayMemberBinding="{Binding XPath='@Amount'}" Header="Amt"/>
  22                     <GridViewColumn x:Name="tbTot" Header="Tot">
  23                         <GridViewColumn.CellTemplate>
  24                             <DataTemplate>
  25                                 <DockPanel>
  26                                     <TextBox Width="100" Text="{Binding XPath='@Tot'}" />
  27                                 </DockPanel>
  28                             </DataTemplate>
  29                         </GridViewColumn.CellTemplate>
  30                     </GridViewColumn>
  31                 </GridView>
  32             </ListView.View>    
  33         </ListView>
  34 
  35         <Label Height="22">
  36             <Label.Content>
  37                 <MultiBinding Converter="{StaticResource mySumConverter}">
  38                     <Binding ElementName="myListView" Path="Items"/>
  39                     <Binding ElementName="myListView" Path="Items.Count"/>
  40                 </MultiBinding>
  41             </Label.Content>
  42         </Label>
  43     </StackPanel>
  44 </Window>

SumConverter

Imports System.Collections.ObjectModel
Imports System.Reflection

<ValueConversion(GetType(Object()), GetType(String))>
Public Class SumConverter : Implements System.Windows.Data.IMultiValueConverter

    Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
        Static lvItems As IList
        Static lvItem As Xml.XmlElement
        Dim nVal As Double

        Convert = 0
        lvItems = values(0)
        If lvItems Is Nothing Then Exit Function

        For Each lvItem In lvItems
            'Debug.Print(lvItem.GetAttribute("Tot"))
            If Double.TryParse(lvItem.GetAttribute("Tot"), nVal) Then
                Convert = Convert + nVal
            End If
        Next

    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
        ConvertBack = Nothing
    End Function
End Class

Get an IWin32Window reference when using WPF (C#)

Sometimes you need a reference to IWin32Window to use components that are compatible with Windows Forms.

  1. To your WPF window, add:

    using System.Windows.Forms;
    using System.Windows.Interop;

     

  2. To your class’s definition add IWin32Window. This denotes that you are implementing the IWin32Window interface:

    public partial class MainWindow : Window, System.Windows.Forms.IWin32Window

     

  3. Implement the Handle method, which is needed to complete an IWin32Window interface.

            public IntPtr Handle
            {
                get
                {
                    var interopHelper = new WindowInteropHelper(this);
                    return interopHelper.Handle;
                }
            }

     

Auto-updating DataGridView

Public Class party
    Dim _contact As String
    Dim _qty As Double
    Dim _amount As Double

    Public Delegate Sub _ContactChanged()
    Public Delegate Sub _QtyChanged()
    Public Delegate Sub _AmountChanged()

    Public Event ContactChanged As _ContactChanged
    Public Event QtyChanged As _QtyChanged
    Public Event AmountChanged As _AmountChanged

    Public Sub New(ByVal Contact As String, ByVal Qty As Double, ByVal Amount As Double)
        Me.Contact = Contact
        Me.Qty = Qty
        Me.Amount = Amount
    End Sub

    Property Contact As String
        Get
            Contact = _contact
        End Get
        Set(ByVal value As String)
            _contact = value
            RaiseEvent ContactChanged()
        End Set
    End Property

    Property Qty As Double
        Get
            Qty = _qty
        End Get
        Set(ByVal value As Double)
            _qty = value
            RaiseEvent QtyChanged()
        End Set
    End Property

    Property Amount As Double
        Get
            Amount = _amount
        End Get
        Set(ByVal value As Double)
            _amount = value
            RaiseEvent AmountChanged()
        End Set
    End Property

End Class

'ViewModel
Public Class parties : Implements INotifyPropertyChanged

    Dim _parties As ObservableCollection(Of party)
    Dim _totalQtyAmount As Double

    Property Parties As ObservableCollection(Of party)
        Get
            Parties = _parties
        End Get
        Set(ByVal value As ObservableCollection(Of party))
            _parties = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Parties"))
        End Set
    End Property

    Property TotalQtyAmount As Double
        Get
            TotalQtyAmount = _totalQtyAmount
        End Get
        Set(ByVal value As Double)
            _totalQtyAmount = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("TotalQtyAmount"))
        End Set
    End Property

    Public Sub New()
        Parties = New ObservableCollection(Of party)
        Parties.Add(New party("Bill Jones", 1, 100.0))
        Parties.Add(New party("John Armond", 2, 300.0))
        Parties.Add(New party("Yasmin LaFont", 1, 100.0))
        Parties.Add(New party("Fred Frankstone", 3, 999.0))

        Dim i
        For i = 0 To Parties.Count - 1
            AddHandler Parties(i).AmountChanged, AddressOf UpdateTotalQtyAmount
            AddHandler Parties(i).QtyChanged, AddressOf UpdateTotalQtyAmount
            AddHandler Parties(i).ContactChanged, AddressOf UpdateTotalQtyAmount
        Next

        UpdateTotalQtyAmount()
    End Sub

    Private Sub UpdateTotalQtyAmount()
        TotalQtyAmount = (From tot In Parties Select tot.Amount * tot.Qty).Sum()
    End Sub

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    Private Sub NotifyPropertyChanged(ByVal info As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
    End Sub
End Class

XAML:

   1         <StackPanel  Grid.Column="1">
   2             <StackPanel.DataContext>
   3                 <me:parties/>
   4             </StackPanel.DataContext>
   5         <Label Content="Parties" FontWeight="Bold" />
   6         <my:DataGrid AutoGenerateColumns="False" 
   7                         x:Name="Aspen_Parties" ItemsSource="{Binding Parties}">
   8             <my:DataGrid.Columns>
   9                     <my:DataGridTextColumn Header="Contact" Binding="{Binding Path=Contact,UpdateSourceTrigger=PropertyChanged}" Width="100">
  10                     <my:DataGridTextColumn.ElementStyle>
  11                         <Style TargetType="{x:Type TextBlock}">
  12                             <Setter Property="TextWrapping" Value="Wrap"/>
  13                         </Style>
  14                     </my:DataGridTextColumn.ElementStyle>
  15                 </my:DataGridTextColumn>
  16                 <my:DataGridTextColumn Header="Q" Binding="{Binding Path=Qty,UpdateSourceTrigger=PropertyChanged}" />
  17                 <my:DataGridTextColumn Header="Tot" Binding="{Binding Path=Amount,StringFormat='C',UpdateSourceTrigger=PropertyChanged}"/>
  18             </my:DataGrid.Columns>
  19         </my:DataGrid>
  20 
  21         <Label Height="22" HorizontalAlignment="Stretch" HorizontalContentAlignment="Right" Content="{Binding Path=TotalQtyAmount}"/>
  22         </StackPanel>

Access an RTF Document that is compiled into an EXE as an embedded resource

To access an RTF document and populate a RichTextBox with its contents:

        Dim txt As New TextRange(TheRichTextBoxToPopulate.Document.ContentStart, TheRichTextBoxToPopulate.Document.ContentEnd)
        Dim _assembly As [Assembly]
        _assembly = [Assembly].GetExecutingAssembly()
        txt.Load(_assembly.GetManifestResourceStream("WpfApplication1.<filename>.rtf"), System.Windows.DataFormats.Rtf)

Binding to Underlying Property of a user control

   1 <UserControl ... x:Name="root"/>

   1 <Label x:Name="lblHeader" Content="{Binding Path=Header, ElementName=root}"/>

In code-behind:

        Public Shared ReadOnly HeaderProperty As DependencyProperty = _
            DependencyProperty.Register("Header", _
                GetType(String), _
                GetType(ListViewWithHeaderControl), _
                New FrameworkPropertyMetadata(New String("Header"), FrameworkPropertyMetadataOptions.AffectsRender))

        Public Property Header As String
            Get
                Header = GetValue(HeaderProperty)
            End Get
            Set(ByVal value As String)
                SetValue(HeaderProperty, value)
            End Set
        End Property

FindName Function and Frames

A window has a FindName function, but it does not search in the content of frames. To access names within frames, do:

Window.GetWindow(Me).FindName("fullReportFrame").Content.FindName("CalculationsFrame").Content.jcData

The Content property should give you the “code-behind” object of the WPF element.