Wpf And design time data part 2–Use a concrete class

The approach to Design Time data shown in previous post is really quick and useful for simple View Models, but in complex application it starts to have some drawback. One of the main problem is remembering the exact syntax to declare object in XAML; when View Models became complex you start to have ObservableCollection of sub ViewModel type, and maintaining design time data can become problematic. The other drawback is that if you rename a property of the viewmodel and you forget to change corresponding design time data, it definition will become invalid.

There are also situation where you cannot use a XAML definition of Design Time data efficiently, because of the MVVM architecture. Suppose a typical scenario where you want to be able to filter a list of objects in MVVM entirely with declarative binding, the easiest solution is the following.

internal ObservableCollection<LogMessageViewModel> _Logs { get; set; }
 
internal CollectionViewSource CvsLogs { get; set; }
 
public ICollectionView Logs
{
    get { return _logs; }
    set { this.Set(p => p.Logs, value, ref _logs); }
}
private ICollectionView _logs;

If you are interested in the pattern behind this piece of code you can read this post that explains how this structure works. With this pattern you should bind the UI element (Ex, a listview) on the Logs property, but since this is a ICollectionView you are not able to declare design time data in XAML. The real collection is usually private and also this pattern requires some code to work because you need to initialize the CollectionViewSource, etc etc. This is usually a big problem, because it greatly reduces the ability to design a good interface for collections of elements. The ability to completely declare in XAML your ViewModel has a limitation because XAML is a simple representation of the serialization of an object so you cannot create a valid ICollectionView with objects inside.

The solution to this problem is quite simple, because you are not forced to use at Design Time the very same class you will use at Run Time, it should only have the same property names. This is the power of WPF binding, if you bind a control of the UI to a property of the DataContext, it does not care of the real type. Lets see how we can use this fact to have a good design experience without the need to use a XAML file to declare an instance of your ViewModel.

This approach to design time data uses another class that inherits from the real View Model so you can populate Design Time data in constructor with standard C# code.

public class RawLoggerViewModelDesignData : RawLoggerViewModel
{
    public RawLoggerViewModelDesignData()
    {
        Logs = new ObservableCollection<LogMessageViewModel>();
        for (int i = 0; i < 10; i++)
        {
            Logs.Add(new LogMessageViewModel()
            {
                Log = new Infrastructure.LogMessage() {
                        Message = "Message " + i,
                        Level = ((LoggerLevel)(i % 6)).ToString().ToUpper(),
                        Timestamp = DateTime.Now,
                }
            });
        }
    }
 
    public new ObservableCollection<LogMessageViewModel> Logs { get; set; }

This approach is simpler, because you are not forced to remember obscure XAML syntax to fill properties of ObservableCollections and you can also leverage all your knowledge and the power of C# to create meaningful design time data. In the previous snippet of code there are a couple of stuff that worth mentions. First of all this object declares a property called Logs and thanks to the keyword it hides the original property of the ViewModel with the same name. This “hiding” property is of ObservableCollection<LogMessageViewModel> type, so it can be populated easily with code. Thanks to this trick you are able to populate Logs property at design time, while at Run Time Logs property is the one declared in the base class, the binding will work but we are able to use Design Time Data. Using this class as design time data is really straightforward, just declare an instance in XAML and the game is done.

<Window.DataContext>
    <designData:RawLoggerViewModelDesignData />
</Window.DataContext>

The other interesting aspect, is the cycle used to populate Lists collection because with simple string manipulation it creates a log for each allowable level, so it will represent all six logger level (OFF, DEBUG, WARN, INFO, ERROR, FATAL). This is the power of using a concrete class for Design Time Data: you can use C# to generate data and you can also use libraries as NBuilder to quickly generates meaningful sample data. Once everything is in place you can simply bind a control (Es datagrid) to the Logs property, design time experience is the very same of the previous example.

image

Figure 1: Design time data experience is the very same of using XAML Design Time Data

You are actually binding to the property of the Design Time View Model, but this is not a problem, at run time the Logs property will resolve to the property of the base ViewModel of ICollectionView type, and everything will work as expected. Now I can format the grid and I want the background color to reflect Logger property of the log, because I want all error message to appear with red background, info with blue background and on. To accomplish this a data trigger is the perfect solution

<Style TargetType="{x:Type DataGridRow}" >
     <Style.Triggers>
         <DataTrigger Binding="{Binding Log.Level}" Value="ERROR">
             <Setter Property="Background" Value="Red" />
             <Setter Property="Foreground" Value="White" />
         </DataTrigger>
         <DataTrigger Binding="{Binding Log.Level}" Value="INFO">
             <Setter Property="Background" Value="Blue" />
             <Setter Property="Foreground" Value="White" />
         </DataTrigger>
   ...
</Style>

Thanks to the fact that I’ve generated a log for each Level property, I’m able to have an immediate feedback on how my UI will looks at runtime for each level type.

image

Figure 2: Immediate feedback of the UI thanks to design time data

Working this way is really productive because you have an immediate feedback of how the UI will like and generating sample data is really easy, since you can use C# syntax and you can hide base class property if some pattern makes some property unusable at design time.

Alk.

SyncronizationContext.Current is not null, still not be able to access UI Controls in WPF

As you probably already know, you can access WPF controls only from a UI Thread and when I use MVVM each PropertyChanged message check for the need to execute on the UiThread to avoid cross-thread problems. Instead of using the Dispatcher in each property changed sometimes you can find code that does a little optimization like this one.

if (SynchronizationContext.Current != null)
    SynchronizationContext.Current.Send(delegate { OnPropertyChanged(propertyName); }, null);

The above code simply check if the Current synchronization Context is not null, if this condition is true, we are in a UI Thread so we can simply raise the OnPropertyChanged event directly in this thread (with the Send method), because it is associated with a UI and we have no need to use the Dispatcher. The else branch was omitted, but actually is a simple use of a saved instance of the UI Dispatcher to raise the property changed event on the WPF ui thread.

After one year, I discovered that sometimes I still got some cross thread exception and if I check the stack trace of logs I verified that the thread that is generating this exception has SyncronizationContext.Current not null, so you start wondering why you still got exception if the code that is raising the PropertyChanged is belonging to the UI.

In this specific situation the source of the problem is due to WebBrowser control because you should know that a Wpf BrowserControl is a wrapper for the standard winform Browser control, thus if you are in some callback of a WebBrowser control (es. DocumentCompleted) you are in a thread with SyncronizationContext.Current not null, but that specific SyncronizationContext cannot access WPF control because it is a Winform one, thus you still have a cross-thread exception.

The simplest solution is capturing a reference to the original SyncronizationContext.Current during startup of the program, and change the above check to verify also if the Current syncronization context is the very same of the startup of the software

if (SynchronizationContext.Current != null && SynchronizationContext.Current == mainSyncContext)  
  SynchronizationContext.Current.Send(delegate { OnPropertyChanged(propertyName); }, null);

In app.xaml.cs, during the startup of the program I grab a reference to the current synchronization context, I store a reference inside a static variable called mainSyncContext and this permits to check if we are in the main UI thread of WPF verifying that the current synchronization context is the very same I got during application startup. To understand what I’m telling you, here is a snapshot of what happens during the execution of the software

image

Figure 1: As you can see I have a current Synchronization context that is not the Wpf Dispatcher one

As you can see in this specific situation I have SyncronizationContext.Current not null, but is not the same context I have at startup of the application. Loking in the stack trace I verified that this code was called from the event handler of OnNavigated event of a WebBrowser control. From Figure1 you can also verify that mainSyncContext is of type DispatcherSyncronizationContext, (the context used in WPF applications) while the current Sync Context is a generic SynchronizationContext, thus confirming that I’m dealing with two different SynchronizationContext in the same application.

Alk.

Wpf Design Time Data

A really cool aspect of WPF is the ability to use DesignTimeData, a feature that added with MVVM pattern gives a unique DesignTime capabilities to programmer and designers. To use Design Time Data you can simply add a new xaml file and use the Build Action DesignData as visible in Figure 1

image

Figure 1: A xaml file with the Build Action of type DesignData.

Now you can simply instantiate your ViewModel inside the code of Design Dat; in a simple LogViewer I have a ViewModel called RawLoggerViewModel that has various properties and I can simply instantiate it in DesignData file, because after all a XAML file is nothing than a serialized object XML representation

<ViewModels:RawLoggerViewModel 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:coll="clr-namespace:System.Collections.ObjectModel;assembly=System"
    xmlns:ViewModels="clr-namespace:LogVisualizer.ViewModels"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:infrastructure="clr-namespace:LogVisualizer.Infrastructure">

    <ViewModels:RawLoggerViewModel.MainFilter>
        Value of the filter
    </ViewModels:RawLoggerViewModel.MainFilter>

 

As you can see I have declared my RawLoggerViewModel in the namespace LogVisualizer.ViewModels, thus I can simply reference the namespace and declare an instance of my ViewModel inside my design time data file. This is only a section of the file but you should check that I’ve set MainFilter Property to the value “value of the filter”, with xaml syntax (it is a standard property of the RawLoggerViewModelClass). Since you are declaring an instance of a real class, Visual Studio has intellisense thus simplifying a lot the construction of Design Time Data.

image

Figure 2: You have full intellisense to create class

This technique permits you to simply create an instance of your view model and populate its properties with data that will be available during design time. Now it is time to instruct the designer that you want to use this specific design time data instance, and this is simply accomplished in the declaration of the Window

ow x:Class="LogVisualizer.Views.RawLoggerView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
	    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:kissMvvm="clr-namespace:LogVisualizer.KissMvvm"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        mc:Ignorable="d"
        d:DataContext="{d:DesignData /DesignTimeData/RawLoggerViewModelSampleData.xaml}"

This will instruct the designer to use the instance declared in the Design Time data file as the data context of the entire window during design time and this will give you some interesting advantages.

The very first advantage of this approach is the ability to select a control, es a TextBox, then click on the little square pointed by the arrow in Figure 3 and choose to data Bind a property to the ViewModel. In figure 3 I’m going to bind the Text Property.

image

Figure 3: Bind a property (in this exampe Text) to a property of the View Model.

This will open a window that permits you to browse all properties of the View Model (Figure 4), and you can choose the property you want bind to the control property.

image

Figure 4: Choose the View Model’s property to use for binding

After you choose the value, you can immediately see the result on the designer.

image

Figure 5: Design time data are immediately shown in the designer

This approach gives you some advantages

  • Less risk of mistyping name of the property entering the Binding syntax in the XAML designer by hand
  • Gives you a visual list of all View Model properties that are available for binding
  • Gives you visual feeling of the result of the binding

If you are interested on really advanced technique on binding in WPF I strongly suggest you to check this series of posts of my friend Mauro, where he is showing a really cool and advanced technique to deal with Design Time Data.

Alk.

Browser control, prevent a link to open in a new page

Working with BrowserControl in .NET can be annoying, especially because it is a really complex control and it is a real browser inside your application. As an example one of the most common problem is how to prevent a page to open links in a new window. Lot of sites have anchor tags with attribute Target=”_blank”, that opens the link on a new tab, leaving the original tab with the original content (es. forum, blog search result, etc).

One of the most common need for an integrated browser control inside own application is permitting to the user to surf the web and offer them some specific functionality, es: highlighting text inside the browser, extract part of the content, manipulate content, extract content etc etc, and when an anchor link has a Target=”_blank” it will open in an entire new regular browser windows, and you have absolutely no control over it. The result is an annoyed user, because he need to copy the link from the new page, and paste back into your application to work with the link. So a standard question arise: “how can I cope with links that are configured to open themselves on a new page?”

A simple solution is manipulate the content of the HTML document simply changing all anchor tags that have a Target attribute and set it to _self. With this simple trick all the links will continue to open on the same window of browser control, without opening a new window. All you need to do is trap the event LoadCompleted.

cntBrowser.LoadCompleted += browser_LoadCompleted;

This is code that works for the WPF version of the Browsercontrol, but it should work with no problem even for original Winform control. Now in the Loadcompleted you need to manage the content of the browser.

HTMLDocument doc2 = cntBrowser.Document as HTMLDocument;

foreach (IHTMLElement item in doc2.links)
{
	var targetAttributeValue = item.getAttribute("target");
	if (targetAttributeValue != null)
	{
		Debug.WriteLine(item.getAttribute("href") as string + " changed href from " + targetAttributeValue as string + " to _self");
		item.setAttribute("target", "_self");
	}
}

code is really simple, cast the Document property of the browser contorl to HTMLDocument class, then iterate in all the links of the document and if the target attribute is present substitute its value with “_self”. This will actually change the content of the page, and each link that is originally created with Target=”_blank” or any other value will become Target=”_self”.

If you use the GeckoWebBrowser control you can use the very same trick, the code is just a little bit different.

if (GeckoBrowser.Document != null && GeckoBrowser.Document.Body != null)
{
    foreach (var anchor in GeckoBrowser.Document.GetElementsByTagName("a"))
    {
        var targetAttributeValue = anchor.GetAttribute("target");
        if (!String.IsNullOrEmpty(targetAttributeValue))
        {
            Debug.WriteLine(anchor.GetAttribute("href") as string + " changed href from " + targetAttributeValue as string + " to _self");
            anchor.SetAttribute("target", "_self");
        }
    }
}

Now each link will open in web browser control, even if it was originally planned to be opened in a new window.

Alk.

FlowDocumentScrollViewer and horizontal stretch

Quite often when you use FlowDocumentScrollViewer it does not use all horizontal space, even if the container is large enough. Here is the declaration of my FlowDocumentScrollViewer

<FlowDocumentScrollViewer
  Background="red" 
  HorizontalScrollBarVisibility="Hidden"
  HorizontalAlignment="Stretch" 
  HorizontalContentAlignment="Stretch" 
  Height="100" 
  Document="{Binding Converter={converter:HtmlToDocumentConverter}}" />  

As you can see I’ve set both HorizontalAlignment and HorizontalContentAlignment to Stretch, the document is inside a Grid with yellow background and here is what I see during run and in designer.

image

Figure 1: FlowDocumentScrollViewer does not use all horizontal spacing

The problem is that FlowDocument does not stretch to occupy all horizontal spacing. As you can see containing grid uses all horizontal space (yellow background helps you to see grid area) while documents use only a little part of available space and the result is a lot of Grid space to remain unused by the FlowDocumentScrollViewer control. The easiest way to solve this problem is binding the width of the FlowDocumentScrollViewer to the same width of the container control

Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource AncestorType=Grid}}"   

Thanks to the RelativeSource binding I’m able to easily obtain the result I want

image

Figure 2: Now my FlowDocumentScrollViewer use all horizontal space.

Gian Maria