Programming‎ > ‎

WPF

I'm learning WPF now (kind of slow start at this time).   As usual, any information useful to me will be listed here.

Updated 7/21/2016

Index


How can I refer resource in a different project?

I wanted to put WPF Resource file in a different project than the application. Here is how. The reference is https://msdn.microsoft.com/en-us/library/aa970069.aspx.

   
  1. I add "StyleResource" project with Resource Dictionary XAML named BlueThemeStyle.xaml at the top.   
  2. Add references to WindowsBase, PresentationCore, PresentationFramework to this project.   
  3. Add reference to the main application this "StyleResource" project.   
  4. Now you can refer to the Resource xaml in a different project as follows:      
     <Window.Resources>        
       <ResourceDictionary Source="/StyleResource;component/BlueThemeStyle.xaml"/>      
     </Window.Resources>
  

Top


How can I create a DataTemplate for a UserControl?

The item below needs datatemplates to switch. If you created UserControls (for dialogs etc.), it is easy to create datatemplate as follows:

    
  <DataTemplate DataType="{x:Type myref:MyViewModel}">        
    <control:MyControl />    
  </DataTemplate>
  
where myref is xmlns:myref="clr-namespace:..." for a view model named "MyViewModel" and control is xmlns:control="clr-namespace:..." for a UserControl named "MyControl".

Top


How can I switch view depending on the property?

I had to show two different dialog based on the property type. It turned out that DataTemplate switching can be done using ContentControl. Here is the way to be done.  You specify two DataTemplates and set the default to be one of them and DataTrigger to switch.  ReceptorEditorViewModel has the property called SelectedReceptorType which returns string, either "Point" or "Grid".   Based on this value, it will show "PointTemplate" or "GridTemplate".

    
  ...        
  xmlns:local="clr-namespace:ReceptorEditorWPF"
  ...        
  <Window.Resources>        
  <DataTemplate x:Key="PointTemplate" >            
    <TextBlock Text="Point Receptor" />        
  </DataTemplate>        
  <DataTemplate x:Key="GridTemplate" >            
    <TextBlock Text="Grid Receptor" />        
  </DataTemplate>        
  <DataTemplate DataType="{x:Type local:ReceptorEditorViewModel}">            
    <ContentControl Content="{Binding}">                
    <ContentControl.Style>                    
	  <Style TargetType="{x:Type ContentControl}">                        
        <Setter Property="ContentTemplate" Value="{StaticResource PointTemplate}" />                        
        <Style.Triggers>                            
	    <DataTrigger Binding="{Binding SelectedReceptorType}" Value="Grid">                                
           <Setter Property="ContentTemplate" Value="{StaticResource GridTemplate}" />                            
        </DataTrigger>                        
      </Style.Triggers>                    
     </Style>                 
    </ContentControl.Style>            
    </ContentControl>        
  </DataTemplate>    
  </Window.Resources>
  Then in Grid, I have the combobox and ContentControl:        
  <StackPanel>            
  <GroupBox Grid.Row="0" Height="30">                
  <ComboBox Name="combo" ItemsSource="{Binding Path=ReceptorTypes}"                          
        SelectedValue="{Binding SelectedReceptorType, Mode=TwoWay}"/>           
  </GroupBox>           
  <GroupBox>                
    <ContentControl Content="{Binding}" />           
  </GroupBox>        
  </StackPanel>
  

Top


How can I resolve 'calling thread must be STA'?

I wanted to change the cursor when reading a big file in WPF. I was firing an event to do so. This caused the InvalidOperation exception. The error message is

  
  A first chance exception of type 'System.InvalidOperationException' occurred in PresentationCore.dll.  
  The calling thread must be STA, because many UI components require this.
  
Note that I change the cursor in the GUI thread and firing event in ViewModel. It is pointing that the calling thread must be STA! The fix is to fire event in Dispatcher thread in the ViewModel as follows:
      
  Application.Current.Dispatcher.BeginInvoke
  (         
    System.Windows.Threading.DispatcherPriority.Send,         
	new Action(() =>         
	{           
	  MyEvent.FireEvent(MyThingChanged, MyEventType.PARSE_START, "Parse Started ...");       
	})      
  );
  

Top


How can I see the DataGrid rows binded to a DataTable?

I wrote a WPF application where DataGrid is bound to DataTable Default View.

   
  <DataGrid ItemsSource="{Binding FileContent, Converter={StaticResource csvConverter}}" 
  AutoGenerateColumns="True" 
  ...
  
where Converter is converting CSV file into DataTable and then return DefaultView. I could not see any rows filled, even though headers were "correct". It turned out that DataGrid binding to DataTable has issues with column header name. Column header name should not have "/", ".", nor a leading space. Once I changed these, I was ableto see rows filled. This problem is caued by the binding declaration parser. See http://msdn.microsoft.com/en-us/library/ms752300.aspx. Here is on how to avoid this issue: http://stackoverflow.com/questions/2940618/what-is-it-about-datatable-column-names-with-dots-that-makes-them-unsuitable-for.

Top


How can I call the member of GUI thread from another thread?

Since WPF runs GUI thread separately, you may encounter the message saying thatyou have to update (...) using the same UI thread. There are two ways of doing this:

  
  1. Using the global Dispatcher Invoke like      
     Application.Current.Dispatcher.BeginInvoke(         
	  new Action(()=> {(things you want to do) }));  
  2. Using Task Library.      
     private readonly TaskScheduler currentScheduler;      
	 ...      
	 // at the ctor you save currentScheduler due to the bug in .NET 4      
	 // you don't need to save under .NET 45 or later      
	 this.currentScheduler = TaskScheduler.FromCurrentSynchronizationContext();      
	 ...      
	 TaskFactory factory = new TaskFactory(this.currentScheduler);      
	 factory.StartNew(()=> {(things you wan to do) } ));
  

Top


Give me a simple example of how binding works?

Download the zip file attached at the end of the page (simplebinding.zip).I demonstrate the use of User Control and the use of viewmodel which is assigned as a datacontext for the user control in XAML.

Top


How can I set busy cursor in WPF?

Do the following in any thread (even in non-UI thread):

   
  1. using System.Windows.Input;   
  2.  Application.Current.Dispatcher.BeginInvoke
      (         
        DispatcherPriority.Normal,         
        new Action(()=> {Mouse.OverrideCursor = Cursors.Wait;})
      );      
	   ...
      (do busy things)      
      Mouse.OverrideCursor = null;
  

Top


How can I signal UI change from non-GUI thread?

Updating UI from non-UI thread requires Invoke we used to do. In the case of TPL Task, you can do this much easier by using CurrentSynchronizationContext. This is from http://reedcopsey.com/2009/11/17/synchronizing-net-4-tasks-with-the-ui-thread/

  
  TaskFactory uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());  
  uiFactory.StartNew(()=>
  { 
    this.tedtBox1.Text = value; 
  });
  

The story is not that simple. Sometimes returned SynchronizationContext.Currentis null. This is recognized as bug in .NET 4.0 TPL and fixed in 4.5 RC. More details is http://stackoverflow.com/questions/4659257/how-can-synchronizationcontext-current-of-the-main-thread-become-null-in-a-windo?rq=1.

Top


How can I create WPF library from plain .NET library?

I built a WPF application but I wanted to move some components into a separate library. I used the project to be a .NET class library but when I tried to compile I get the error message saying that ApplicationDefinition cannot be used. Here is the solution. See also http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/d10de84c-bef1-46ed-8127-7ad19b8eec37/.

  
  1. Right Click on the Xaml file included in the class library project.  
  2. Change Build Action from ApplicationDefinition to Page.  
  3. Add the following four references: WindowsBase, Presentation.Core,      
     Presentation.Framework,System.Xaml.
  

Top


How can I write EventTrigger of Button in XAML?

I thought that I could use "Event Triggers" to remove code-behind Button.Click handler. However, I can only find examples of StoryBoard. I could not find example of Setter in response of EventTrigger. Nick Kramer of MSFT stated ( http://www.vistax64.com/avalon/752-concept-template-triggers-eventtriggers.html) that Right or wrong (and I've heard arguments both ways), we decided not tosupport setters inside event triggers because unlike every other form oftrigger, the setter would continue to apply even after thestyle/template/whatever containing the trigger had been removed. I thought that this is crazy. Fortunately, Expression Blend SDK comes to help me.BlendSDK has the necessary tags: Interaction.Triggers with EventTrigger and ChangePropertyAction. Here is the way:

 
  1. Add two references from Blend SDK 4.0:     
     xmlns:i="clr-namespace:System.Windows.Interactivity;              
	 assembly=System.Windows.Interactivity"     
	 xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;              
	 assembly=Microsoft.Expression.Interactions" 
  2. A simple example of okButton event handler   
     <Button Name="okButton" Content="OK">      
        <i:Interaction.Triggers >        
          <i:EventTrigger EventName="Click">          
            <ei:ChangePropertyAction TargetName="EView" PropertyName="DialogResult" Value="True" />        
          </i:EventTrigger>      
        </i:Interaction.Triggers>    
     </Button>
  

Top


How can I responds to the list view change?

When the WPF app is done by MVVM way, the addition to the list view in View is handled by binding ViewModel. Thus, View cannot tell directly whether the view list has been updated. Usually View will be notified the event. Unfortunately WPF tab control seems not to have such CollectionChanged event like Form ListView has. It turned out that it is hidden and thus you have to expose the event as follows (Tab Control is named as "Workspaces" in the code):

  
  // show the new workspace just added  
  var view = CollectionViewSource.GetDefaultView(this.Workspaces.Items);  
  view.CollectionChanged += (s, e) =>   
  {     
    if (e.NewItems != null)        
	this.Workspaces.SelectedItem = e.NewItems[0];   
  };
  

Top


How can I create a dialog with validation?

There are many articles talking about DataValidation in WPF, but only a few providesthe complete solution. In particular, many articles talks about changing Style forvalidation errors. I only one article discussing disabling the OK button when theerror occurs. Here is the way. After IDataErrorInfo in ViewModel, you add the TextBox binding with


  View XAML:  
  <Grid.CommandBindings >    
  <!-- bind the default command called "Close", i.e. no need to create ICommand -->      
  <CommandBinding Command="Close"                            
  CanExecute="OK_CanExecute"                            
  Executed="OK_Executed"  />  
  </Grid.CommandBindings >      
  ...  
  <TextBox     
     Validation.Error="Validation_Error",    
	 Text="{Binding (boundprop), ValidatesOnDataErrors=True, NotifyOnValidationError=True, ...}"   
  <Button Content="OK" Command="Close" /> 
  
  View CS  
  private void Validation_Error(object sender, ValidationErrorEventArgs e)  
  {   
    if (e.Action == ValidationErrorEventAction.Added)     
	  ++noOfErrors;   
    else     
	  --noOfErrors;  
  }   
  
  private void OK_CanExecute(object sender, CanExecuteRoutedEventArgs e)  
  {    
    e.CanExecute = (noOfErrors == 0);    
	e.Handled = true;  
  }  
  
  private void OK_Executed(object sender, ExecutedRoutedEventArgs e)  
  {    
    this.DialogResult = true;    
	// let WPF close the dialog after this setting  
  }
  

Top


How can I bind the combobox?

It is easy to bind properties, but I wondered how I can bind Combobox.Here it is


  In View XAML file:     
  <ComboBox HorizontalAlignment="Left"                  
        Name="myComboBox" ItemsSource="{Binding ServiceList}"                   
		DisplayMemberPath="Key"  
		SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>
  
  In ViewModel,     
  public KeyValuePair SelectedItem    
  {      
    get { return selectedItem; }      
	set       
	{         
	  selectedItem = value;        
	  this.OnPropertyChanged("SelectedItem");      
	}    
  }    
  
  public Dictionary<string, string> ServiceList    
  {      
     get { return Services; }      
	 set       
	 {         
	   Services = value;        
	   this.OnPropertyChanged("ServiceList");      
	 }    
  }
  

Top


How can I set the icon for the WPF application?

WPF needs to specify two icons.  One with the title bar and the minimized taskbar icon.  You can set this icon in Properties → Application, but it does not show it under debugging. Once you set it, your icon is available under your project and thus you set it under MainWindow XAML file as follows. Now the icon is set in debug mode also.  Another icon must be set for the one to be shown in File Explorer.   That one is set by Properties -> Application ->Icon and Manifest.

  
  <Window ... Icon="myicon.ico" />
  

Top


How can I fix the slow 64bit WPF under .NET 4?

Here is the page you want to read. http://social.msdn.microsoft.com/Forums/en-AU/netfx64bit/thread/476e21c4-6b73-4f8e-bfab-a7aaae5b017f. You may sometimes have to do ngen (native code generator).

  
  Windows\Microsoft.NET\Framework64\v4.0.30319\ngen update
  
Note that I got lot of exceptions, not finding the right assemblies, but still it did some work.

Top

Yasunari Tosa,
Jun 27, 2013, 6:38 AM
v.1