Month List

RecentComments

Comment RSS

Porting a WPF application to Windows8

by Ioannis 19. October 2011 18:18

(for a brief intro in Windows8 visit first this post)

In this post, we will take a simple application implemented in WPF for Windows (Vista/XP SP3) and try to change it in a Windows8 “Metro” style application. During this process, we will evaluate two things:

 

  • If the application runs as is (the executable) in Windows8 when it is compiled in Visual Studio 2010 in Windows 7.
  • If we want to change the application for the new “Metro” style will it compile directly or will we need to do some changes and which and how big are those?

 

This demo is performed on a virtual machine running the Windows8 Developer preview presented in this post. Therefore keep in mind that we may be drawing some conclusions based on a “preview” product that will not reflect the complete reality. Also the implementation for Windows8 will be performed with the Visual Studio 2011 Developer Preview that runs from within Windows8. This edition allows only a particular kind of projects to be implemented called “Metro” style which are the ones for the new Windows8 UI. This does not mean that Visual Studio 2011 will not support “WPF Applications”. If you install the Visual Studio 2011 Preview for Windows7 all your loved project templates are there. In this post though, we want to see if our current knowledge can be used to implement the “new style” applications and therefore we target the new templates.

A screenshot of the application we are going to implement is shown below:

 

imageimage

 

We have a list of clients displayed in a Listbox at the top of the window. Whenever a name is selected, its details are displayed at the bottom and can be edited. If we click on the button “Validate”, the client data are validated and a message is displayed showing the result of the validation process. Our entity in this application is represented by a class named Client:

 

public class Client : INotifyPropertyChanged {
   private int _id;
   public int Id {
      get { return _id; }
      set { _id = value; 
            if (PropertyChanged != null) { 
                PropertyChanged(this, new PropertyChangedEventArgs("Id")); 
                PropertyChanged(this, new PropertyChangedEventArgs("Description")); } }
   }
   private string _name;
   public string Name {
      get { return _name; }
      set { _name = value; 
            if (PropertyChanged != null) { 
                PropertyChanged(this, new PropertyChangedEventArgs("Name")); 
                PropertyChanged(this, new PropertyChangedEventArgs("Description")); } }
   }
   public string Description {
      get { return this.ToString(); }
   }
   public override string ToString() {
      return String.Format("{0,5} {1,-40}", Id, Name);
   }
   public event PropertyChangedEventHandler PropertyChanged;
}
 

That is, our class implements the INotifyPropertyChanged interface and also raises the notification for the “Description” property whenever the Id or the Name properties are changed in order for the Listbox contents (databound to Description) to be refreshed. Our ViewModel for the application is as follows:

 

public class MainWindowViewModel : INotifyPropertyChanged
{
   private List<Client> _clients;
   public List<Client> Clients
   {
      get { return _clients; }
      set { _clients = value; 
         if (PropertyChanged != null) 
             PropertyChanged(this, new PropertyChangedEventArgs("Clients")); }
    }
    private Client _selectedClient;
    public Client SelectedClient
    {
       get { return _selectedClient; }
       set { _selectedClient = value; 
          if (PropertyChanged != null) 
              PropertyChanged(this, new PropertyChangedEventArgs("SelectedClient")); }
     }
     public MainWindowViewModel() {}
     public void Validate() {
        if (SelectedClient != null) {
           if (String.IsNullOrWhiteSpace(SelectedClient.Name))
              MessageBox.Show("Name cannot be empty for client");
           else
              MessageBox.Show("All ok");
        }
     }
     public event PropertyChangedEventHandler PropertyChanged;
}

 

Again, the ViewModel implementes the INotifyPropertyChanged interface and has the Clients property which will be databound to the ItemSource of the Listbox and the SelectedClient property which will be databound to the SelectedItem of the Listbox and to the detail Textboxes at the bottom of the window. Moreover, it provides the Validate method for validating the results. The XAML file describing the UI of the window is as follows:

 

<Window x:Class="AskADev.Article1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Demo WPF porting to Windows8" Height="400" Width="544" Loaded="Window_Loaded">
    <DockPanel>
        <Canvas DockPanel.Dock="Bottom" Height="100" Background="Beige">
            <TextBlock Text="Id" Canvas.Left="6" Canvas.Top="12" />
            <TextBox Text="{Binding SelectedClient.Id}" Canvas.Left="79" 
                                                        Canvas.Top="9" Width="72" />
            <TextBlock Text="Name" Canvas.Left="6" Canvas.Top="40" />
            <TextBox Text="{Binding SelectedClient.Name}" Canvas.Left="79" 
                                                          Canvas.Top="37" Width="311" />
            <Button x:Name="ButtonSubmit" Height="31" Width="73" Canvas.Left="443" 
                   Canvas.Top="63" Click="ButtonSubmit_Click">Validate</Button>
        </Canvas>
        <ListBox DockPanel.Dock="Top" ItemsSource="{Binding Clients}" 
                 SelectedItem="{Binding SelectedClient}" DisplayMemberPath="Description"/>
    </DockPanel>
</Window>

 

Where you can see the data bound properties. Finally the XAML.cs file initializes the ViewModel, attaches it to the DataContext and also provides the event handler for the button:

 

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    ViewModel = new MainWindowViewModel();
    DataContext = ViewModel;
}
private void ButtonSubmit_Click(object sender, RoutedEventArgs e)
{
   ViewModel.Validate();
}
 

We compile this and run it on a Windows7 machine and it runs correctly. Now we start the Windows8 Virtual Machine (instructions on how to install Windows8 on your desktop can be found in this post), get the executable we have created and run it on Windows8 “as is”:

image

Obviously, as expected, the application runs without any problems. But it runs on “Desktop” mode, while we want to make it run on the nice new Metro-Style UI. We open Visual Studio 2011 from within Window8 and select New Project/Templates/Visual C#/Windows Metro Style/Application:

image

The windows in this project template are more “Silverlight” like having as their root content a UserControl and not a Window. Therefore we leave the root declaration intact and copy/paste the inner contents of the original XAML.cs file. The changes we need to make are as follows (all changes are characterized as major/moderate/minor based on the time it will take you to make the changes in a full blown WPF app):

 

  • 1 (moderate) The Window must be converted to UserControl.
  • 2 (major) The “Dockpanel” is not supported (which is kinda expected since we will have a fixed size full screen application) and therefore we change it to a “Canvas” layout.
  • 3 (major) The default mode for the binding in now “One Way”. Therefore we need to specify the binding explicitly to “TwoWay”.
  • 4 (major) Textboxes get enlarged in the new “Metro” style (since they are intended for touch UIs) therefore we need to increase the spacing between the Textboxes (add approximately 10pixels). Of course if we had initially used a Stackpanel for the placement of our controls this would not be needed (good design always handles change more easily Smile)
  • 5 (minor) We need to change the colors to reflect the new style (I chose darker shades)

 

The new XAML is as follows (changes in yellow):

 

<UserControl x:Class="AskADev.Article1.Windows8.MainPage"
    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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Loaded="Window_Loaded"
    d:DesignHeight="768" d:DesignWidth="1366">

    <Canvas>
        <Canvas Canvas.Top="668" Width="1366" Height="100">
            <TextBlock Text="Id" Canvas.Left="6" Canvas.Top="12" />
            <TextBox Text="{Binding SelectedClient.Id,Mode=TwoWay}" 
                     Canvas.Left="79" Canvas.Top="9" Width="72" />
            <TextBlock Text="Name" Canvas.Left="6" Canvas.Top="50" />
            <TextBox Text="{Binding SelectedClient.Name,Mode=TwoWay}" 
                     Canvas.Left="79" Canvas.Top="47" Width="311" />
            <Button x:Name="ButtonSubmit" Height="31" Width="73" Canvas.Left="443" 
                    Canvas.Top="63" Click="ButtonSubmit_Click">Validate</Button>
        </Canvas>
        <ListBox Background="Black" Foreground="White" Width="1366" Height="668" 
                 ItemsSource="{Binding Clients}" 
                 SelectedItem="{Binding SelectedClient,Mode=TwoWay}" 
                 DisplayMemberPath="Description"/>
    </Canvas>
</UserControl>
 

The XAML.CS file (apart from some minor changes in the name) remains the same.

Finally, the ViewModel’s file is added as is to the application. The issues that we encounter here are the following:

 

  • 6 (major) The MessageBox is not supported. Therefore you will need to find another way to inform your users about the result (probably from within a special place of the UI). In this app we will just comment it out and do nothing. In real life we would have to change the UI to have a “Messages” area.
  • 7 (minor – but annoying until you discover it) Your application will compile if you leave the INotifyPropertyChanged interface to be defined in the System.ComponentModel assembly as it is for WPF. But the binding will not work! You need to replace the “using System.ComponentModel” statement with “using Windows.Ui.Xaml.Data” which also defines the INotifyPropertyChanged interface and the bindings will magically work!

 

We recompile and run the up and we are in “Metro”!

image

 

Shout it

Tags:

WPF | Windows8

ListBoxes with DataBound Images in WPF

by Ioannis 12. January 2011 21:19

In this post, we see how we can add a DataBound image in the DataTemplate of a WPF ListBox. We experiment with three different approaches of achieving that and discuss on which one to use depending on the situation. In all cases, the image files are embedded within the application as “Resource” files. Later in this post, we will see what needs to be changed in order to support having the image files in a folder outside of the application Assembly.

 

Our object for binding will be the class named Item having the following properties:

 

public class Item
{
    public int PictureID { get; set; }
    public string Name { get; set; }
}

 

We want to display a specific image depending on the value of the property PictureID. The ListBox in the .xaml file be:

 

<ListBox ItemTemplate="{DynamicResource DataTemplateItem}" ItemsSource="{Binding Items}"/>

 

The definition of the DataTemplate for the items of the ListBox is:

 

<DataTemplate x:Key="DataTemplateItem">
    <Canvas Width="236" Height="48">
        <Image Source="{Binding ??}" Height="32" Width="32" Canvas.Left="8" Canvas.Top="8"/>
        <TextBlock Text="{Binding Name}" Canvas.Left="56" Canvas.Top="8" Width="172"/>
    </Canvas>
</DataTemplate>

 

The question in hand is what we need to put in the Binding of the Source property of the Image element in order to get the following result:

image                               image

 

The Images reside in the “Images” folder of the project and have their “Build Action” set to “Resource”.

 

1) Extending the Item class

Our first approach is to introduce a new property within the class Item that generates the appropriate string for the image path within the assembly. So the new extended Item class is:

 

public class Item {
    public int PictureID { get; set; }
    public string Name { get; set; }
    public string PictureString {
        get {return "/Images/" + PictureID.ToString() + ".png";}
    }
}

 

Note the new property PictureString with its accessor calculating the appropriate image path. Now in the Source property of the DataTemplate we just need to add Source=”{Binding PictureString}”.

 

2) Using DataTemplate Triggers

A different approach that leads to the same result is not changing at all the Item class and introducing the following style in the item’s DataTemplate:

 

<DataTemplate x:Key="DataTemplateItemStyle">
    <Canvas Width="236" Height="48">
        <Image Name="ImageName" Height="32" Width="32" Canvas.Left="8" Canvas.Top="8"/>
        <TextBlock Text="{Binding Name}" Canvas.Left="56" Canvas.Top="8" Width="172"/>
    </Canvas>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding PictureID}" Value="0">
            <Setter TargetName="ImageName" Property="Source" Value="Images\0.png"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding PictureID}" Value="1">
            <Setter TargetName="ImageName" Property="Source" Value="Images\1.png"/>
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

 

In this DataTemplate, the Source property is not directly DataBound to any property. We use a DataTemplate trigger to test the value of the PictureID property and based on the result decide which image to display.

 

3) Using a Converter

The third approach uses a Converter to do the job. The converter is written as follows:

 

public class ImageConverter:IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, 
                                                        System.Globalization.CultureInfo culture) {
        return "/Images/" + value.ToString() + ".png";
    }

    public object ConvertBack(object value, Type targetType, object parameter, 
                                                             System.Globalization.CultureInfo culture){
        throw new NotImplementedException();
    }
} 

 

And the DataTemplate will become:

 

<local:ImageConverter x:Key="ImageConverter"/>
<DataTemplate x:Key="DataTemplateItemConverter">
    <Canvas Width="236" Height="48">
        <Image Source="{Binding PictureID,Converter={StaticResource ImageConverter}}" (…) />
        <TextBlock Text="{Binding Name}" Canvas.Left="56" Canvas.Top="8" Width="172"/>
    </Canvas>
</DataTemplate>

 

The Converter receives the PictureID as it arrives from the object and converts it accordingly.

 

Which approach we use?

 

If you cannot change the Item class (because it is not your code for example) you cannot use the first approach unless you create a wrapper for the class and provide the appropriate extended property. If you do not want to do this, you selection of one of the two other approaches depends on how complex your image selection logic is. If it is complicated then the Data trigger approach may be difficult or impossible to implement and then your only choice is resorting to the converter.

 

On the other hand if you can alter the properties of the Item class then you can extend your class with the new property. Sometimes, though, your image selection logic does not depend only on properties of your business object and therefore you do not want to extend your POCO entities with the general business logic of you system. If this is the case a converter is either your best choice, or an extension of the Item class in the View augmented with the appropriate logic.

 

Now if your images are not resources in the Assembly and reside somewhere in the server where you deploy your application you need to change the paths for all approaches to: “pack://siteoforigin:,,,/Images/0.png” where the “pack://siteoforigin:,,,” says “the root of the application” and then you provide the path for the image.

 

Download the project here

 

Finally in case you are wondering in the project the property IsSynchonizedWithCurrentItem=true maintains the same selection for all the three ListBoxes in the project since they are bound to the same list of Item objects.


 

Shout it


 

Entity Framework, the Context, ComboBoxes and Include Foreign Key columns.

by Ioannis 9. September 2010 07:48

How do they all connect? Well it boils down to the following questions:

 

  • If I am using Entity Framework in a Desktop application when should I open the Context and for how long should I keep it open?
  • I have ComboBoxes that will be used to select values for the Reference Properties of my Entity Framework objects. How am I going to achieve this?
  • Should I bind to Reference Properties or should I include the Foreign Keys in my model (option named “Include Foreign Key columns in the model”) and bind to it from my ComboBoxes?

 

Let’s take for example to following DB schema and its Entity Framework model.

image image 

That is, Products are sold to Users in specific dates and those sales are stored in the Sales table. Note that in this model I have chosen to “Include Foreign Key columns in the model” and I also have the Navigation Properties (Reference Properties) available. The application presents us with a list of the Sales made by date and allows us to Edit/Add/Delete a Sale. When we click the Edit or Add button, a new window opens with the selected Sale’s details.

 “When and for how long should I leave the context open in this scenario?”

The Object Context in Entity Framework follows the “Unit of Work” pattern. The “Unit of Work” pattern states that  one should perform all changes to his business objects and then commit all those changes with one round-trip to the database. Those changes are related to each other by a common “business task” that needs to be done. In EF’s case, this means that all changes made to the objects belonging to a Context are going to be submitted to the database altogether when we call SaveChanges(), as a single database transaction (that is if one command fails all changes will be rolled over). Therefore a decision needs to be made about the time we open and close the Context. We distinguish 3 options:

 

  • “One Context Per Application” means that we open the Context when the application starts and whenever we need to persist our changes to the database we call the SaveChanges() method. The Context will be disposed when the application ends. This in most cases is not a good option. For example, in an application where you can have multiple windows open you can be editing let’s say two Sales at the same time. If these two Edit Windows share the same Context the Sale objects that are data bound to them will also belong to the same Context. This means that if you click Save in one of the two calling SaveChanges()  you will in fact be saving all the changes for the other Edit Window too. This can be tested in the EFCBTests1 project. Opening two “Edit Sale” windows changing some of their values and clicking “Save” in one of the two, will also persist the changes made to the other. Moreover, following this approach makes it really difficult transforming your application to “Service Oriented” since there is no way of keeping the context open throughout different service calls. If your application is really small and mainly opens modal windows or consists only of reads and not writes to the database it is the only case where this approach may suit you. In all other cases you must avoid it.

 

  • “One Context Per Method” means that in every method you open and close the context with a using statement. This is the extreme of the previous case and although valid does not allow you to take advantage of any of the goodies that come out of the box with you entities. For example your data bound to the GUI objects are not in the context and therefore you always need to attach them before persisting them. The situation gets even worse when you also want to control a list of objects in the GUI allowing the user to add/remove items. If the list is not in the context you have extra work to do later when you want to persist the changes. This approach also in long business transactional operations will tend to create huge methods for you since you will find yourself implementing a lot of login within that using statement. Therefore this approach is useful in Web applications although in that case the “One context per Http Request” is a better approach. Otherwise, it is not recommended.

 

  • “One Context Per Window” means that we open a new Context whenever a new Window is displayed to the user. The Context gets disposed when the Window closes. This is more close to the “Unit of Work” pattern, since we consider a Window as a single, simple task that the user needs to accomplish. This is the most recommended approach in Desktop applications. Of course sometimes you can break this rule by allowing the Context to be shared among windows (especially when the one is opened by the other and is modal).  If for example in the list of Sales you allow only one edit window to be open you could pass the context from the “List window” to the “Edit window” since you could consider the two windows as a single “Task” for your application. In fact the advantages of sharing a Context between windows is that whatever you do with your Context objects in one of your windows gets immediately reflected to the other. This is a neat feature (provided to you under the hoods by the INotifyPropertyChanged interface that the default Entity Framework objects implement – not the POCO ones) since it allows you to have fresh data in all of your open windows. This could be a good choice in EFCBTests1 project if the Edit window was opened as modal and for only one sale. Generally, this is considered the best approach for Desktop applications.

 

Therefore to summarize for Desktop applications:

 

  • Do not use “One Context Per Application” except for the cases where your application is really small, opens only modal windows (one window at a time) and it is not going to become large in the future.
  • Follow the “One Context Per Window” rule since in most of the cases the Window defines your “Unit of Work”, open the Context when opening the window and it will get disposed by itself when you close it. This also allows you to just close the window when the user hits “Cancel” having an “Undo” feature out of the box since all the changes will be lost with the Context.
  • Break the “One Context Per Window” rule in situations where you will open another window which you can consider as “part” of the task that needs to be done in the parent window. In this case you get the out of the box features of sharing the same objects between windows but always keep in mind that changes will be persisted throughout all open windows.

 

Having stated that the “One Context per Window” approach seems to be the best one for Desktop applications we face two challenges, the one of binding Navigation Properties to ComboBoxes or to other selection controls that have lists of selection objects and the other of keeping some global objects that we need to use as defaults for the Navigation Properties of our Entities.

 

“How do I bind my ComboBoxes, ListBoxes etc to Navigation Properties and how do I use default global objects as values for my Navigation Properties?”

In the EFCBTests2 project we open a new Context in the Edit Sales window and load the Products and Users entities that will be ItemsSources for our Combos from within that context. Data Binding and SaveChanges() work like a charm since the objects that are referenced by the Navigation Properties of the Sales object and are data bound to the Combos are in the same Context. But beware. Whenever the window opens a new round-trip occurs to the database to fetch all the Products and Users. This imposes an overhead and in cases where your data does not expect to change that often (having a selection list of Countries or Currencies for example) it is bad design and scales poorly. You need to be able to load in your application the Users and the Products only once (eg. at the beginning)  and use the in-memory lists whenever the Edit Sales window opens without visiting your database every time.

But if you load the lists at the beginning, they will belong to a different context and since the context will close they will become Detached Entities. If you now set them as the ItemsSources of your Combos and bind the Combos  to Navigation Properties, when you call SaveChanges you end up creating new objects since the ones that are listed by the Combos are considered new to the current context.

The same happens when you want to have a global object that you need to use as the default one for a Navigation Property throughout the application (an example will follow later in this post)

To tackle all this you have two choices:

 

1) “Include Foreign Key columns in the model” approach

Use the option “Include Foreign Key columns in the model”, load the source lists of the Combos in a static class and in the Edit Sales window make databinding work with the FK ids of the Sales object and not with the Navigation Properties. This is the approach followed in EFCBTests3 project. We use the Inititalizations static class to load the lists once:

public static class Initializations {
    public static List<Product> Products { get; set; }
    public static List<User> Users { get; set; }
 
    static Initializations(){
        using (Entities Ctx = new Entities()){       {
            Ctx.Users.MergeOption = MergeOption.NoTracking;
            Ctx.Products.MergeOption = MergeOption.NoTracking;
            Products = Ctx.Products.OrderBy(x => x.Title).ToList();
            Users = Ctx.Users.OrderBy(x => x.Username).ToList();
        }
    }
}

Note that the MergeOption directive allows as to create Detached entities from the beginning. Bind your combos in XAML in the Edit Sales window as follows:

<ComboBox ItemsSource="{Binding Products}" 
          DisplayMemberPath="Title" 
          SelectedValuePath="Id" 
          SelectedValue="{Binding TheSale.ProductSold_Id,Mode=TwoWay}" 
/>

Note that the ItemsSource property is set to list that references the static list (this connection is made at the .xaml.cs file), the SelectedValuePath is set to the primary key Id and the SelectedValue is databound to the FK ProductSold_Id (the foreign key included in the Entity).

Using the foreign keys for databinding enables you also to create lists of objects to use as ItemsSources that are more lightweight than the full blown ones. For example in ECBTests4 project we create a ProductLite class as follows:

public class ProductLite {
    public int Id { get; set; }
    public string Description { get; set; }
}

And we create the Products static list using those objects in the static Initializer:

Products = (from p in Ctx.Products orderby p.Title ascending 
            select new ProductLite() {
                    Id=p.Id,
                    Description=p.Title
            }).ToList();

As you can see with this approach we save a lot of memory and we still can bind to our Entity Framework objects. So why should we bother with another approach (explained later in this post) that does not use the foreign keys for binding? Well, a lot of people would argue that the whole foreign key thing is a database artifact and should appear as little as possible in the Business Logic or the UI. If the Business Layer and the GUI are unaware of these FK artifacts it is a lot easier to migrate to other kinds of databases or change the Primary and Foreign key types.

“Is there another reason to use the foreign keys in my Entity objects?”

Another reason you may need the Foreign Key is when you intend to store an Entity object during the whole application lifecycle and use it when persisting other entities. A representative example is the case in the following db and the equivalent EF schema.

image image

When the application initializes we want to load a specific Currency. Then throughout the whole application whenever a new ProductsCur is created by the user, we need to set  its Currency to the global Currency object. Clearly the default Currency object is not part of the same Context of the one that will be created for the new product. Therefore we cannot use it as it is. This problem is solved with the foreign key inclusion since we can use that instead. In EFCBTests5 project we do exactly that. In the Initializations we have the DefaultCurrencyId which gets its value at the initiation of the application:

public static class Initializations {
    public static int DefaultCurrencyId;
 
    static Initializations()  {
        using (Entities Ctx = new Entities()) {
            DefaultCurrencyId = Ctx.Currencies.First().Id;
        }
    }
}

Now in the Edit Product window we bind the Currency Combo to the FK as in the previous case and whenever we initialize a new product we go as follows:

TheProduct = new ProductsCur() { ProductName="",Price=0,
                 CurrencyID=Initializations.DefaultCurrencyId};
_ctx.ProductsCurs.AddObject(TheProduct);

So we have seen two very important uses of the Foreign key Id. How would we proceed if in both these cases we do not want to include the foreign keys in the model?

2) Not including any foreign keys approach

We have to tackle two cases. How to data bind to ComboBoxes and how to use global objects such as the Currency object. In the first we do not want to load the values of the Combo every time the window opens but we want to have the list of objects available from initialization. To tackle this we need to implement a new ComboBox, specific for the currencies. The ComboBox will display the static list of the currencies but whenever the selected value is changed it will use the new Context to fetch the object from the database. The static list is initializes as follows:

public static class Initializations {
    public static Currency DefaultCurrency;
    public static List<Currency> Currencies;
 
    static Initializations()  {
        using (Entities Ctx = new Entities()) {
            Ctx.Currencies.MergeOption=MergeOption.NoTracking;
            DefaultCurrency = Ctx.Currencies.First();
            Currencies = Ctx.Currencies.OrderBy(x => x.Code).ToList();
        }
    }
}

Now we create the CurrenciesComboBox as follows:

public class CurrenciesComboBox:ComboBox
{
    public Entities Context
        {
            get { return (Entities)GetValue(ContextProperty); }
            set { SetValue(ContextProperty, value); }
        }
    public static readonly DependencyProperty ContextProperty =
        DependencyProperty.Register("Context", typeof(Entities), typeof(CurrenciesComboBox), 
        new UIPropertyMetadata(null));
 
    public Currency SelectedCurrency
        {
            get { return (Currency)GetValue(SelectedCurrencyProperty); }
            set { SetValue(SelectedCurrencyProperty, value); }
        }
    public static readonly DependencyProperty SelectedCurrencyProperty =
        DependencyProperty.Register("SelectedCurrency", typeof(Currency), typeof(CurrenciesComboBox), 
        new FrameworkPropertyMetadata(_onSelectedCurrencyChanged));
 
    private static void _onSelectedCurrencyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){
        CurrenciesComboBox Sender = d as CurrenciesComboBox;
        Currency NewCurrecny=e.NewValue as Currency;
        Sender.SelectedItem = Initializations.Currencies.FirstOrDefault(x => x.Id == NewCurrecny.Id);
    }
    public CurrenciesComboBox(){
        if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
        {
            ItemsSource = Initializations.Currencies;
            DisplayMemberPath = "Code";
            SelectedValuePath = "Id";
        }
    }
    protected override void OnSelectionChanged(SelectionChangedEventArgs e){
        if (SelectedItem == null)
            SelectedCurrency = null;
        else
        {
            Currency Temp = (SelectedItem as Currency);
            SelectedCurrency = Context.Currencies.FirstOrDefault(x => x.Id == Temp.Id);
        }
        base.OnSelectionChanged(e);
    }

We declare two dependency properties, one for the Context Binding (Context) and one for the Navigation Property (SelectedCurrency) binding. It is very important to first declare the Context since WPF assigns the bindings from top to bottom. If you do it the other way round you will get a null reference exception.

The main idea is that when SeletedCurrency changes we select from the Currencies static list the appropriate item and set it as the SelectedItem. When the SelectedItem changes we search in the context and find the appropriate item and set it as the SelectedCurrency. In the constructor we do the connection with the static list and the GetInDesignMode method allows our control to appear without problems in the VS Designer. This approach also allows us to work with Lite objects as in the previous scenario with small modification on what we select on each case.

After we create it, we use the CurrenciesComboBox as follows:

<Window x:Class="EFCBTests.WindowEditAddProduct"
        ...
        xmlns:app="clr-namespace:EFCBTests"
        ...>
...
<app:CurrenciesComboBox Context="{Binding Ctx}" 
                SelectedCurrency="{Binding TheProduct.Currency,Mode=TwoWay}"/>
...
</Window>

Finally if we want to use a global default currency object, whenever we need to create a new Entitiy in a context the Currency Navigation Property should be defined as follows (previously where we display the Initializations class note that the DefaultCurrency object is created with the MergeOption set to NoTracking which means that it is detached from the beginning):

TheProduct = new ProductsCur() 
             {ProductName="",Price=0,
              Currency=Ctx.Currencies.First(x=>x.Id==Initializations.DefaultCurrency.Id)};

This approach is used in EFCBTests6 project.

This concludes a pretty large article on the Entity Framework and Context and some tricks in binding Navigation Properties to controls of the GUI.

Projects to download:

To run the project you will need also the following backup of the SampleApp database created in MSSQL 2008 (Database Backup File)

Shout it

kick it on DotNetKicks.com

Powered by BlogEngine.NET 2.0.0.36

ITPRO DevConnections 2011

Creative Commons License

Programming Blogs - BlogCatalog Blog Directory Add to Technorati Favorites

MVP Award


Ioannis Panagopoulos





This blog is using BlogEngine.Net and is hosted in the hoster below. I have not experienced any problems installing BlogEngine.Net in the host and I am satisfied with the host's response times. Therefore I recommend it.


DiscountASP Add