Showing posts with label wpf 4. Show all posts
Showing posts with label wpf 4. Show all posts

Friday, March 25, 2011

WPF: Configuring System.Runtime.Caching.MemoryCache for an MVVM based App

This is a continuation of our post here:

http://shujaatsiddiqi.blogspot.com/2011/03/wpf-performance-improvement-for-mvvm.html

In the above post we discussed how we can use the new caching feature available in .net framework 4.0 to improve the performance of a WPF application. As an example we used an application implementing MVVM pattern. In this example we would extend the example by discussing how we can use configuration option provided with this caching feature. We would also look at the underlying architecture provided by the framework to support this.

As we discussed in the previous example, the framework has provided MemoryCache. This is an implementation of ObjectCache providing an in-memory caching mechanism. Let us create a simple view showing the fields from this cache.

In order to use this you need to add a reference of System.Runtime.Caching assembly from the framework.

Yes I have Power Tools installed :).
<Window x:Class="WPF_MVVM_Caching.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WPF_MVVM_Caching"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Label Content="PollingInterval" Height="26" HorizontalAlignment="Left" 
               Margin="10,21,0,0" Name="label1" VerticalAlignment="Top" Width="141" />
        <TextBlock Height="26" HorizontalAlignment="Left" Margin="157,21,0,0" Name="txtPollingInterval"
                   Background="LightGoldenrodYellow"
                   Text="{Binding PollingInterval, Mode=OneTime}" VerticalAlignment="Top" Width="302" />
        <Label Content="PhysicalMemoryLimit" Height="26" HorizontalAlignment="Left" 
               Margin="10,53,0,0" Name="label2" VerticalAlignment="Top" Width="141" />
        <TextBlock Background="LightGoldenrodYellow" Height="26" HorizontalAlignment="Left" 
                   Margin="157,53,0,0" Name="txtPhysicalMemoryLimit" Text="{Binding PhysicalMemoryLimit, Mode=OneTime}" 
                   VerticalAlignment="Top" Width="302" />
        <Label Content="Name" Height="26" HorizontalAlignment="Left" 
               Margin="11,85,0,0" Name="label3" VerticalAlignment="Top" Width="140" />
        <TextBlock Background="LightGoldenrodYellow" Height="26" HorizontalAlignment="Left" 
                   Margin="157,85,0,0" Name="txtName" Text="{Binding Name, Mode=OneTime}" 
                   VerticalAlignment="Top" Width="302" />
        <Label Content="CacheMemoryLimit" Height="26" HorizontalAlignment="Left" 
               Margin="10,116,0,0" Name="label4" VerticalAlignment="Top" Width="141" />
        <TextBlock Background="LightGoldenrodYellow" Height="26" HorizontalAlignment="Left" 
                   Margin="157,116,0,0" Name="txtCacheMemoryLimit" Text="{Binding CacheMemoryLimit, Mode=OneTime}" 
                   VerticalAlignment="Top" Width="302" />
    </Grid>
</Window>

The view is expecting certain properties from MainWindowViewModel which is used as its DataContext. They are bound with Text properties of TextBlocks. The definition of MainWindowViewModel is as follows:
namespace WPF_MVVM_Caching
{
    using System.ComponentModel;
    using System.Runtime.Caching;

    class MainWindowViewModel : INotifyPropertyChanged
    {
        public string CacheMemoryLimit
        {
            get { return string.Format("{0}", MemoryCache.Default.CacheMemoryLimit); }
        }

        public string PollingInterval
        {
            get { return string.Format("{0}", MemoryCache.Default.PollingInterval); }
        }

        public string PhysicalMemoryLimit
        {
            get { return string.Format("{0}", MemoryCache.Default.PhysicalMemoryLimit); }
        }

        public string Name
        {
            get { return MemoryCache.Default.Name; }
        }

        #region INotifyPropertyChanged Implementation
        
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        private void onPropertyChanged(string propertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion INotifyPropertyChanged Implementation
    }
}


Loading Default Memory Cache with Configuration Options:
The configuration of default MemoryCache is easy. We just need to provide the following code in the app.config file and the framework automatically loads the MemoryCache with the specified values as in the configuration file.
<?xml version="1.0"?>
<configuration>
  <system.runtime.caching>
    <memoryCache>
      <namedCaches>
        <add name="Default"
             cacheMemoryLimitMegabytes="52"
             physicalMemoryLimitPercentage="40"
             pollingInterval="00:04:01" />
      </namedCaches>
    </memoryCache>
  </system.runtime.caching>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>
Now run the application. We can see that the MemoryCache.Default is constructed with the values that we specified in the configuration file. The view would appear as follows:


So in order to provide the definition of the Default MemoryCache configuration, we just need to make sure that we use name Default and provide the values for the properties that we want to change. The properties not specified in configuration would be kept with default values.

Configuring Non-Default Caches:
In the above example we have seen how we can configure the Default MemoryCache. We can use the same technique to configure a non-default cache. We might be needing it when we want to define a local cache. In order to instantiate a non-default cache, whose properties expected to be loaded from a configuration file, MemoryCache provides a constructor. We need to provide the name of cache configuration to use to construct the cache instance as follows:
<?xml version="1.0"?>
<configuration>
  <system.runtime.caching>
    <memoryCache>
      <namedCaches>
        <add name="TestCache"
             cacheMemoryLimitMegabytes="52"
             physicalMemoryLimitPercentage="40"
             pollingInterval="00:04:01" />
      </namedCaches>
    </memoryCache>
  </system.runtime.caching>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>
We can use this cache as follows:
class MainWindowViewModel : INotifyPropertyChanged
{
    MemoryCache cache;

    public MainWindowViewModel()
    {
        cache = new MemoryCache("TestCache");
        //string s = "";
    }

    public string CacheMemoryLimit
    {
        get { return string.Format("{0}", cache.CacheMemoryLimit); }
    }

    public string PollingInterval
    {
        get { return string.Format("{0}", cache.PollingInterval); }
    }

    public string PhysicalMemoryLimit
    {
        get { return string.Format("{0}", cache.PhysicalMemoryLimit); }
    }

    public string Name
    {
        get { return cache.Name; }
    }

    #region INotifyPropertyChanged Implementation
    
    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    private void onPropertyChanged(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion INotifyPropertyChanged Implementation
}
When we run the application we can see that the properties are successfully loaded from the configuration and the view is shown as follows:


System.Runtime.Caching.Configuration in Details
Now let us look at the underlying architecture used by the above logic. The configuration classes for run-time caching is provided in System.Runtime.Caching.Configuration namespace in System.Runtime.Caching assembly. They are based on the same logic we implement when we want configuration option for some classes i.e. We have to define classes inheriting from the following classes:

- ConfigurationElement
- ConfigurationElementCollection
- ConfigurationSection
- ConfigurationSectionGroup

For System.Runtime.Caching, these classes are as follows:
- MemoryCacheElement
- MemoryCacheSettingsCollection
- MemoryCacheSection
- CachingSectionGroup

They can be shown in the class diagram as follows:



Now in order to go further with this example we need to add a reference of System.Configuration assembly as follows:


Let us update the configuration file as follows:
<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="MemoryCacheConfiguration"
                  type="System.Runtime.Caching.Configuration.MemoryCacheSection, System.Runtime.Caching, Version= 4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </configSections>
  <MemoryCacheConfiguration>
    <namedCaches>
      <add name="MyMemoryCache" physicalMemoryLimitPercentage="23"
            pollingInterval="00:04:43" cacheMemoryLimitMegabytes="123" />
    </namedCaches>
  </MemoryCacheConfiguration>
</configuration>
Here we have defined a section named MemoryCacheConfiguration for System.Runtime.Caching.MemoryCacheSection type. Later we have provided the details about MemoryCacheConfiguration with details about runtime cache.

Now we instantiate a memory cache based on the information provided here in the configuration file. We can update the view model's constructor as follows:
public MainWindowViewModel()
{
    //Get Configuration section info from Configuration
    MemoryCacheSection section = (MemoryCacheSection)ConfigurationManager.GetSection("MemoryCacheConfiguration");

    //Create a name / value pair for properties
    NameValueCollection configurationNameValueCollection = new NameValueCollection();
    configurationNameValueCollection.Add("pollingInterval", 
        string.Format("{0}", section.NamedCaches[0].PollingInterval));
    configurationNameValueCollection.Add("physicalMemoryLimitPercentage", 
        string.Format("{0}", section.NamedCaches[0].PhysicalMemoryLimitPercentage));
    configurationNameValueCollection.Add("cacheMemoryLimitMegabytes", 
        string.Format("{0}", section.NamedCaches[0].CacheMemoryLimitMegabytes));
    
    //instantiate cache
    cache = new MemoryCache(section.NamedCaches[0].Name, configurationNameValueCollection);
}
Now let us run the application. The view should appear as follows:


Download Code:

Saturday, March 5, 2011

WPF - Binding InputBindings to View Model Commands using MVVM

In this post we will be discussing about how we can bind mouse and key gestures directly with Commands in the view model. Generally when we talk about this type of binding we end up using RoutedCommand directly in the view having Execute and CanExecute methods in the view itself. This is a new feature of WPF 4.

In order to understand this, let's consider a sample application with some input bindings. When the particular key combination are received by WPF runtime, it should update properties in view model. These properties supports change notifications by implementing INotifyPropertyChanged interface.
<Window x:Class="WpfApplication_InputBindings_MVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication_InputBindings_MVVM"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Window.InputBindings>
        <KeyBinding Key="F2" Modifiers="Control" Command="{Binding OperationCommand}" />
    </Window.InputBindings>    
    <Grid>
        <Label Content="Message" Height="26" HorizontalAlignment="Left" Margin="12,47,0,0" 
               Name="label1" VerticalAlignment="Top" Width="67" FontWeight="Bold" />
        <TextBlock Height="27" HorizontalAlignment="Left" Margin="86,53,0,0" 
                   Name="textBlockMessage" Text="{Binding Message}" VerticalAlignment="Top" Width="405" />
    </Grid>
</Window>
In the above view we have added KeyBinding for "Ctrl + F2" key combination. The view model should have a command OperationCommand. When user presses the specified key combination, it should execute the command methods on the view model. The view is using a new instance of MainWindowViewModel as its DataContext. It is also binding Text property of the only TextBlock to a property Message from the DataContext.

Let's look at the definition of MainWindowViewModel. Since it needs to support Property change notification to the view, it is using one of the available change notification system. It is using this mechanism for its Message property. As part of its contract for INotifyPropertyChanged, it provides an event PropertyChanged. We have used RelayCommand for supporting command execution from view model [http://msdn.microsoft.com/en-us/magazine/dd419663.aspx].
class MainWindowViewModel : INotifyPropertyChanged
{
    #region Operation Command
    
    RelayCommand _operationCommand;
    public ICommand OperationCommand
    {
        get
        {
            if (_operationCommand == null)
            {
                _operationCommand = new RelayCommand(param => this.ExecuteCommand(),
                    param => this.CanExecuteCommand);
            }
            return _operationCommand;
        }
    }

    public void ExecuteCommand()
    {
        Message = "Command Executed";
    }

    bool CanExecuteCommand
    {
        get { return true; }
    }
    
    #endregion Operation Command

    #region Properties

    private string _message;
    public string Message
    {
        get { return _message; }
        set
        {
            _message = value;
            OnPropertyChanged("Message");
        }
    }

    #endregion Properties

    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion INotifyPropertyChanged implementation
}
When we run the application and hit the specified key combination, it first checks if the command is eligible for execution from CanExecuteCommand property. If it returns true then it executes ExecuteCommand method. In this method we are setting Message property to "Command Executed". Since this property supports change notification so the same changes are updated on the view.

Let us run this now. The application is displayed as follows:


As expected, When we press Ctrl + F2 key combination the view is updated as follows:


Stealing Key Gestures from Application Commands:
Still we have simple command bound to the Window. What if we have a command with key gestures from default Commands available by the framework including ApplicationCommands? Now let's progress this example into a little complex one. Let's change the input bindings to a key gestures Ctrl + V.
<Window x:Class="WpfApplication_InputBindings_MVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication_InputBindings_MVVM"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Window.InputBindings>
        <KeyBinding Key="V" Modifiers="Control" Command="{Binding OperationCommand}" />
    </Window.InputBindings>    
    <Grid>
        <Label Content="Message" Height="26" HorizontalAlignment="Left" Margin="12,47,0,0" 
               Name="label1" VerticalAlignment="Top" Width="67" FontWeight="Bold" />
        <TextBlock Height="27" HorizontalAlignment="Left" Margin="86,53,0,0" 
                   Name="textBlockMessage" Text="{Binding Message}" VerticalAlignment="Top" Width="405" />
        <TextBox Height="29" HorizontalAlignment="Left" Margin="82,152,0,0" Name="textBox1" 
                 VerticalAlignment="Top" Width="409" />
    </Grid>
</Window>
In the above XAML, we have updated the key gestures to Ctrl + V. We move focus to the new TextBox and hit Ctrl + V [I have text "Muhammad Siddiqi" copied to clip board].


You can see that TextBox has stolen the key gestures. This has resulted in the text on the clip board being pasted to the Text Box. This is because of default paste command using this key gesture for the text box.

Binding Key Gesture of un-wanted command to ApplicationCommands.NotACommand:
The first solution is very simple. We can just assign those key gestures to ApplicationCommands.NotACommand. We can achieve the same using CommandManager available in System.Windows.Input namespace. Let's update the constructor of the view as follows:
public MainWindow()
{
    InitializeComponent();

    CommandManager.RegisterClassInputBinding(typeof(UIElement),
        new InputBinding(ApplicationCommands.NotACommand, 
            new KeyGesture(Key.V, ModifierKeys.Control)));
}
Now let's run the application again. When we hit Ctrl + V the command actually executes and key gestures are not stolen by Paste Command for the text box.


This approach seems to be working but it has a few limitations. First this is application wide i.e. this gesture is stolen for the whole application. The other thing is that we can not un-register this.

Vote: http://connect.microsoft.com/VisualStudio/feedback/details/481627/no-way-to-unregister-a-commandmanager-registerclasscommandbinding

Handling CanExecute Tunneling Event:
The other solution is to handle PreviewCanExecute event of the command causing the key gesture conflict. In the following example we are using CommandBinding to provide tunneling event handler for CanExecute for Paste Command. Since CommandBindings, unlike InputBindings, is a DependencyProperty if this command is handled above in the logical hierarchy this wouldn't be propagating down. Let's update XAML as follows:
<Window x:Class="WpfApplication_InputBindings_MVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication_InputBindings_MVVM"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Window.InputBindings>
        <KeyBinding Key="V" Modifiers="Control" Command="{Binding OperationCommand}" />
    </Window.InputBindings>    
    <Window.CommandBindings>
        <CommandBinding Command="Paste" PreviewCanExecute="CommandBinding_PreviewCanExecute" />
    </Window.CommandBindings>
    <Grid>
        <Label Content="Message" Height="26" HorizontalAlignment="Left" Margin="12,47,0,0" 
               Name="label1" VerticalAlignment="Top" Width="67" FontWeight="Bold" />
        <TextBlock Height="27" HorizontalAlignment="Left" Margin="86,53,0,0" 
                   Name="textBlockMessage" Text="{Binding Message}" VerticalAlignment="Top" Width="405" />
        <TextBox Height="29" HorizontalAlignment="Left" Margin="82,152,0,0" Name="textBox1" 
                 VerticalAlignment="Top" Width="409" />
    </Grid>
</Window>
Let's see the definition of CommandBinding_PreviewCanExecute:
private void CommandBinding_PreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.Handled = true;
}
If we run this now and hit Ctrl + V by setting focus on the text box, this event handler is executed. Since this is handled here this would not be propagated to the text box. The view is updated as follows:


Download Code: