POCO Support in Entity Framework 4
In this post I am going to explain a small experiment I did with Entity Framework CTP4. My intention was to create some Entity classes and use it with the code first approach provided with EF4.
Till now I haven’t touched EF because I am not a big fan of auto generated code and config files generated by EF. When I saw some article about the new features of EF that comes with v4 release, especially the code first approach and POCO support. I thought of wetting my hands in EF 4. POCO support in EF 4 was really good, no auto generated codes, no edmx files, you can write pretty clean codes.
Let’s jump into the code. My project is not structured just very plain. Entities, Context and UI,.. every thing in single project. As I said this is just a tracer to see how things works.
Table Structure
CREATE TABLE [dbo].[User](
[ID] [int] IDENTITY(1,1) NOT NULL,
[UserCode] [varchar](10) NOT NULL,
[UserName] [varchar](50) NOT NULL,
[Password] [varchar](50) NOT NULL,
[UserEmail] [varchar](50) NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE[dbo].[ImageComment](
[CommentId] [int] IDENTITY(1,1) NOT NULL,
[CommentDescription] [varchar](max) NULL,
[UserName] [varchar](50) NULL,
[UserId] [int] NOT NULL,
[CommentDate] [datetime] NOT NULL,
CONSTRAINT[PK_ImageComments] PRIMARY KEY CLUSTERED
(
[CommentId] ASC
)WITH(PAD_INDEX =OFF,STATISTICS_NORECOMPUTE =OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS =ON,ALLOW_PAGE_LOCKS =ON)ON[PRIMARY]
)ON[PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE[dbo].[ImageComments] WITH CHECK ADD CONSTRAINT[FK_ImageComments_Users] FOREIGN KEY([UserId])
REFERENCES[dbo].[Users] ([ID])
GO
User.UserId is a foreign key in ImageComment.
Entity Classes
public class User { public User() { this.Comments = new List<UserComment>(); } [Key,StoreGenerated(StoreGeneratedPattern.Identity)] public int UserID { get; set; } public string UserCode { get; set; } public string UserName { get; set; } public string Password { get; set; } public string UserEmail { get; set; } public ICollection<UserComment> Comments { get; set; } }
public class UserComment { public UserComment() { } [Key, StoreGenerated(StoreGeneratedPattern.Identity)] public int CommentId { get; set; } public string Comment { get; set; } public virtual int UserId { get; set; } public virtual User User1 { get; set; } public DateTime CommentDate { get; set; } }
Nothing much to explain just plain Entity classes. To add Key Attribute (just above the identity column) I added the System.ComponentModel.DataAnnotations;name space to the above classes. You might think that why I added Key Attribute to the indentity, the reason is EF4 POCO supports on Conventions. In some scenarios it may not identify the key property and will throw error “Unable to infer a key for entity type”. We need to attach [Key] to mark the identity key for EF.
Let’s see the Configuration classes.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Objects; using Microsoft.Data.Entity.Ctp; using System.Data.Entity.ModelConfiguration; namespace EntityFrameWorkTracer_CodeFirstModel { public class UserConfigurationen:EntityConfiguration<User> { public UserConfigurationen() { Property(c => c.UserID).IsIdentity(); Property(c => c.UserName).HasMaxLength(50).IsRequired(); Property(c => c.UserCode).HasMaxLength(10).IsRequired(); Property(c => c.Password).HasMaxLength(50).IsRequired(); Property(c => c.UserEmail).HasMaxLength(50); this.HasMany(c => c.Comments); this.MapHierarchy(c => new { UserName = c.UserName, UserCode = c.UserCode, Password = c.Password, UserEmail = c.UserEmail, ID = c.UserID }).ToTable("Users"); } } public class CommentConfiguration : EntityConfiguration<UserComment> { public CommentConfiguration() { Property(c => c.CommentId).IsIdentity(); Property(c => c.Comment).HasMaxLength(5000); this.HasRequired(c => c.User1).WithMany(a => a.Comments).WillCascadeOnDelete().HasConstraint((c, y) => c.UserId == y.UserID); this.MapHierarchy(c => new { CommentId=c.CommentId, CommentDescription = c.Comment, UserId = c.UserId, }).ToTable("ImageComments"); } } }
The configuration class is the important piece of code here. This configuration class maps the Entity to the respective tables. We can also specify the meta property of the column like Required property, size of the field, etc.
You can see one important line of code in CommentConfiguration (marked in bold italics). It specifies the relation between UserComment and User table.
Object Context class
The derived object context class is used of Saving and Retrieving data from the db.
using System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Data.Objects;
usingSystem.Data.EntityClient;
namespaceEntityFrameWorkTracer_CodeFirstModel
{
public classAWModel:ObjectContext
{
publicAWModel(EntityConnection connection)
: base(connection)
{
}
publicIObjectSet<User> User
{
get{ return base.CreateObjectSet<User>(); }
}
}
}
UI Code
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Data.SqlClient; using System.Data.Entity.ModelConfiguration; namespace EntityFrameWorkTracer_CodeFirstModel { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { try { SqlConnection con = new SqlConnection("Data Source=SQLserver;Initial Catalog=ImagePublisher;User ID=sony;PWD=sony;MultipleActiveResultSets=True;"); var builder = new ModelBuilder(); builder.Configurations.Add(new UserConfigurationen()); builder.Configurations.Add(new CommentConfiguration()); var model = builder.CreateModel(); var context = model.CreateObjectContext<AWModel>(con); context.ContextOptions.LazyLoadingEnabled = false; var query = from c in context.User select c; foreach (var u in query) { context.LoadProperty(u, "Comments"); //Explicity loads Comments property in User object. MessageBox.Show(u.UserName);foreach (UserComment comment in u.Comments) { MessageBox.Show(comment.Comment); } }//Uncomment and see how data save will work //User usr = new User //{ // UserName = "Kevin", // Password = "Kevin", // UserEmail = "Kevin@gmail", // UserCode = "gh" //}; //context.User.AddObject(usr); //context.SaveChanges(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } } }
I struggled a lot to figure out how to load the related objects, like Comments in the User object. My architect forwared a MSDN link and solved my puzzle. From the link I got to know about the Context.LoadProperty and implemented, viola it worked.
CTP4 supports the above code only support of configuration, before that you have to provide the edmx file as the configuration. Download CTP4.
Let’s see how to achieve Many to Many relation ship in POCO model
Many to Many Relation ship
To experiment many to many relation ship I added a new table to my db, Role and UserRoles as shown below
I modified the User entity to add the below property.
public ICollection<Role> UserRoles { get; set; }
This property holds all the Roles associated to the User.
Role Entity is very straight forward as shown below
using System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.ComponentModel.DataAnnotations;
namespaceEntityFrameWorkTracer_CodeFirstModel
{
public classRole
{
publicRole()
{
this.Users = newList<User>();
}
[Key]
public int RoleId { get; set; }
public stringRoleName { get; set; }
publicICollection<User> Users { get; set; }
}
}
The important piece of code is the RoleConfiguration class. Let see the configuration class
public classRoleConfiguration : EntityConfiguration<Role>
{
publicRoleConfiguration()
{
Property(r => r.RoleId).IsIdentity();
this.HasMany(r => r.Users).WithMany(u => u.UserRoles).Map(“dbo.UserRoles”, (role, user) =>new
{
RoleId=role.RoleId,
UserId=user.UserID
});
this.MapHierarchy(r => new
{
RoleId=r.RoleId,
RoleName=r.RoleName
}).ToTable(“Role”);
}
}
The code I marked in bold italics is our area of interest. Here I specified relationship between Role.Users and User.Roles. Also I specified the mapping table that holds the relation, here my mapping table is UserRoles. RoleId, RoleName, UserId I marked in big font is nothing but the columns in the respective tables in the db. It is a mapping between Table columns and entities property.
Persisting Combo box SelectedItem – Caliburn Micro
In this post I am going to explain how to persist Selected Item in a Combo box. In my application I have several screens and the user can navigate between them. I need to persist the Combobox’s Selected Item and bind it back when user navigate back to the screen. Here persisting is not db level persistence.
My application is based on Caliburn Micro, a wonderful lightweight MVVM framework.
To persist the selections, I wont create new ViewModel when ever the user clicks on the Navigation button. Only once the instance of the view model will get created. The following code shows the implementation, very simple approach.
FontPropertiesViewModel _fontPropertiesViewModel; public void GetFontProperties() { if (_fontPropertiesViewModel == null) _fontPropertiesViewModel = new FontPropertiesViewModel(); ActiveScreen = _fontPropertiesViewModel; }
<UserControl x:Class="MVVMTracer.Pages.Views.FontPropertiesView" 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" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ca="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro" mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot" Background="Transparent" Margin="0,0,10,0"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="25"/> <RowDefinition Height="35"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="35"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="150"/> <ColumnDefinition Width="0.555*"/> </Grid.ColumnDefinitions> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Text" VerticalAlignment="Center" FontSize="13.333" Margin="10,0,0,0" Foreground="#FF0F5DA7" Grid.ColumnSpan="2" Width="297"/> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Font Type" Margin="10,0,0,0" Grid.Row="2" FontSize="11" VerticalAlignment="Center"/> <ComboBox x:Name="FontType" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" FontSize="11" SelectedItem="{Binding SelectedFontType}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <ca:ActionMessage MethodName="FontTypeSelectionChanged"> <ca:Parameter Value="{Binding ElementName=FontType,Path=SelectedItem}"></ca:Parameter> </ca:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </ComboBox> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Font Size" Margin="10,0,0,0" Grid.Row="3" FontSize="11" VerticalAlignment="Center"/> <ComboBox x:Name="FontSize" Margin="0,0,0,0" Grid.Row="3" FontSize="11" HorizontalAlignment="Left" VerticalAlignment="Center" Width="53" Grid.Column="1" SelectedItem="{Binding SelectedFontSize}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <ca:ActionMessage MethodName="FontSizeSelectionChanged"> <ca:Parameter Value="{Binding ElementName=FontSize,Path=SelectedItem}"></ca:Parameter> </ca:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </ComboBox> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Font Style" Margin="10,0,0,0" Grid.Row="4" VerticalAlignment="Center" FontSize="11"/> <ComboBox x:Name="FontStyle" Grid.Row="4" Grid.Column="1" FontSize="11" VerticalAlignment="Center" SelectedItem="{Binding SelectedFontStyle}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <ca:ActionMessage MethodName="FontStyleSelectionChanged"> <ca:Parameter Value="{Binding ElementName=FontStyle,Path=SelectedItem}"></ca:Parameter> </ca:ActionMessage> </i:EventTrigger> </i:Interaction.Triggers> </ComboBox> <Button x:Name="Done" Content="Done" Grid.Row="5" Grid.Column="1" Width="70" HorizontalAlignment="Right" d:LayoutOverrides="Height" VerticalAlignment="Bottom"/> </Grid> </Grid> </UserControl>
If you examine the xaml you can see that the selected item of the combo boxes are binded to different properties, also I use ActionMessaging to notify the view model when ever a selection change happen in the Comboboxes. You might wonder why am using ActionMessaging rather than I could bind SelectedItem in two way mode. Yes I tried it first but unfortunately the Selected Item is not showing when user navigates back to the page.
Time to go through the view model
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Caliburn.Micro; using MVVMTracer.MockServices; using EmblemDesigner.Controls; namespace MVVMTracer.Pages.ViewModels { public class FontPropertiesViewModel:PropertyChangedBase { public FontPropertiesViewModel() { this.FontType = FontServices.GetFontFamily(); this.FontStyle = FontServices.GetFontStyle(); } private List<string> _fontType; public List<string> FontType { get { return _fontType; } private set { _fontType = value; NotifyOfPropertyChange(() => FontType); } } private string _selectedFontType; public string SelectedFontType { get { return _selectedFontType; } private set { _selectedFontType = value; } } public void FontTypeSelectionChanged(object selectedItem) { this.SelectedFontType = selectedItem as string; } private List<string> _fontSize; public List<string> FontSize { get { return _fontSize; } private set { _fontSize = value; NotifyOfPropertyChange(() => FontSize); } } private string _selectedFontSize; public string SelectedFontSize { get { return _selectedFontSize; } private set { _selectedFontSize = value; } } public void FontSizeSelectionChanged(object selectedItem) { this.SelectedFontSize = selectedItem as string; } private List<string> _fontStyle; public List<string> FontStyle { get { return _fontStyle; } private set { _fontStyle = value; NotifyOfPropertyChange(() => FontStyle); } } private string _selectedFontStyle; public string SelectedFontStyle { get { return _selectedFontStyle; } private set { _selectedFontStyle = value; } } public void FontStyleSelectionChanged(object selectedItem) { this.SelectedFontStyle = selectedItem as string; } } }
Let’s take an eg. of FontType to understand what I am doing here. In the View one of my Combo box name is FontType. Caliburn Micro is intelligent enough to bind the FontType properties in the view model to the Combo box with name FontType in the view. Really great. If you run the application, you can see the FontType combo box is populated with the fonts we added to the FontType properties.
But it’s not enough to persist the user selection. To do so we need to add some more code to the view model. You might have noticed in xaml that the ActionMessage of FontType combo is attached to FontTypeSelectionChanged. When ever the user change selection it will get notified to FontTypeSelectionChanged function in the view model. This function will intern update the SelectedFontType property. The selected SelectedFontType property is binded to FontType’s Combobox’s SelectedItem.
Below diagram shows the communication between the view and the view model what I explained above.
One thing I noticed here is the Auto Implemented or Automatic Properties wont work here.
If any better approach pls update me.
An Experiment In Caliburn Micro
In this blog I will explain my experiment with Caliburn Micro. The business logic of this app is
- It should display a list of images
- Add the image to the canvas when the user selected an image
- Drag the Image and Position the Image where ever the user required
- For dragging/rotation/resize the image I used an Adorner control. You can get more details about the control here.
I have to add Images displayed in a list box to a canvas. Pretty simple 🙂 and helped me a lot to learn Caliburn Micro. Before going further I wanted to say that the app may not be a full MVVM model, you will come to know as you proceed. I am working on it to make it completely MVVM complaint.
Below is the screen shot of the Silverlight app.
Two buttons are there on the top.
- Show Text: button will load User control that will have a UI with some text box and combo box.
- Image Controls: Button will load the user control displayed above.
Bare with my naming, I am very poor in it.
When the user clicks on any of these button we need to show the respective View in the left hand side. I used Caliburn Micro as my MVVM frame work.
My project structure is below
- Views: Contains my XAML files
- ViewModels: contains my view model of the views
- ValueObject: contains the value object class I used in this app
- Messages: I used EventAggregator provided by Caliburn Micro to enable messaging between ViewModels. This folder contains the Class I used for messaging between ViewModels.
- Controls: contains custom controls I used in this app.
- Adorner: contains the Adorner controls
- Let’s jump into the code
TabView
This is the main view which calls other other user controls- XAML
<navigation:Page x:Class="MVVMTracer.Views.TabView" 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" xmlns:tl="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit" xmlns:we="clr-namespace:MVVMTracer.Controls" mc:Ignorable="d" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="640" d:DesignHeight="480" Title="TabView Page"> <UserControl.Resources> <DataTemplate x:Key="shapeTemplate"> <we:ControlBase Width="{Binding Width}" Height="{Binding Height}"/> </DataTemplate> </UserControl.Resources> <Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="40"></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal" Height="30"> <Button x:Name="ShowTextControls" Content="Show Text"></Button> <Button x:Name="ShowImageControls" Content="Image Controls"></Button> </StackPanel> <tl:TransitioningContentControl Grid.Row="1" Grid.Column="0" x:Name="ActiveScreen"></tl:TransitioningContentControl> <we:CustomItemsCollection x:Name="DisplayControls" Grid.Row="1" Grid.Column="1"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas Background="AliceBlue"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </we:CustomItemsCollection> </Grid> </navigation:Page>
In the above xaml you might notice that I used TransitioningContentControl. This is the control I am going to use to show different UserControl based on the user request. By the way TransitioningContentControl is part of Silverlight Tool kit,
Here I used a CustomItemsCollection to add my dynamically added controls. CustomItemsCollection is a control derived from ItemsControl. I will show the implementation of it later.
TabViewModel.cs
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Caliburn.Micro; using MVVMTracer.Adorners; using MVVMTracer.Controls; using MVVMTracer.Messages; namespace MVVMTracer.ViewModels { public class TabViewModel:PropertyChangedBase,IHandle<Messages.ImageSelectionChanged> { public TabViewModel() { EventAggregatorContainer.EventAggregator.Subscribe(this); } private object _activeScreen; private Adorner _adorner = new Adorner(); private Canvas _parentCanvas = new Canvas(); public object ActiveScreen { get { return _activeScreen; } set { _activeScreen = value; NotifyOfPropertyChange(() => ActiveScreen); } } public void ShowTextControls() { ActiveScreen = new ShowTextViewModel(); } public void ShowImageControls() { ShowImageControlViewModel imgViewModel = new ShowImageControlViewModel(); ActiveScreen = imgViewModel; } public void Handle(Messages.ImageSelectionChanged message) { ControlBase ctrl = ShapeFactory.GetShape(ShapeType.Image, _parentCanvas, _adorner); ctrl.Source = message.CurrentImage; this.AddToUICollection(ctrl); } private void AddToUICollection(ControlBase control) { if (_displayControls == null) { _displayControls = new System.Collections.ObjectModel.ObservableCollection<UIElement>(); _adorner.Height = 0; _adorner.Width = 0; _parentCanvas.Children.Add(_adorner); _displayControls.Add(_parentCanvas); } control.DrawControl(); control.Height = 100; control.Width = 100; _parentCanvas.Children.Add(control); NotifyOfPropertyChange(() => DisplayControls); } private System.Collections.ObjectModel.ObservableCollection<UIElement> _displayControls; public System.Collections.ObjectModel.ObservableCollection<UIElement> DisplayControls { get { return _displayControls; } private set { _displayControls = value; NotifyOfPropertyChange(() => DisplayControls); } } } }
The above code explains the ViewModel of TabView. As I mentioned in the beginning it may not completely satisfies MVVM pattern. I will explain the reason. As per my requirement I wanted to add Images to my Canvas and allow the user to reposition it.
To achieve the requirement I initially binded list of UIElement that consist of my Images to the CustomItemsCollection. But the issue I faced is I couldn’’t drag the image to different location. So I find out a work around. The work around is create a Canvas in Viewmodel and add the canvas to DisplayControls. Then add the images to the canvas object. By adding Canvas to DisplayControls will internally bind the Canvas object and its children to CustomItemsCollection in the View.
In this app I used EventAggregator to enable Messaging between ViewModels. To enable Messaging in CaliburnMicro the subscriber should Implement IHandle interface. You can see in the above code I implemented IHandle interface and subscribed to ImageSelectionChanged. Any publisher who publish ImageSelectionChanged message will be notified to TabViewModel.
The custom Control to display Image is not in MVVM structure.
You can find the Image listing control and other piece of codes in the uploaded source code.
You can download the code here
Callouts in Silverlight
As per the project requirement I have to show some Callouts. I was thinking of creating it using paths and curves. It would have been a lot of work. But blend saved my time. Blend provides callouts and other shapes in toolbar, VS editor wont provide it in toolbar. Below xaml shows a callout with an Oval textbox (Oval textbox I mentioned in my earlier post).
Name Space: System.Windows.Shapes
XAML
<UserControl 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" xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing" xmlns:we="clr-namespace:Designer.Controls" mc:Ignorable="d" x:Class="Designer.Controls.CallOutOval" d:DesignWidth="640" d:DesignHeight="480"> <Grid x:Name="LayoutRoot"> <ed:Callout AnchorPoint="0,1.25" CalloutStyle="Oval" Fill="#FFF4F4F5" FontSize="14.666999816894531" Stroke="Black" HorizontalAlignment="Left" VerticalAlignment="Top"> <ContentControl> <we:OvalTextBox Text="Your Text here" CornerRadius="50" BorderThickness="0" Background="Transparent"></we:OvalTextBox> </ContentControl> </ed:Callout> </Grid> </UserControl>
Blend provides different callouts like Oval, Rectangle and Cloud. We are done with the Callout user control.
Image dragging and dropping in Silverlight
In this sample code I am going to demonstrate how we can drag and drop an image from windows explorer to a Silverlight page.
To run the test we should have a page with an Image control. Lets call our Image control as ImgShow. In the xaml, set the AllowDrop property to true. Also add a drop event to the Image control. Once the drop event is added go to the code behind and add the below code to the event.
private void ImgShow_Drop(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { FileInfo[] fi = (FileInfo[])e.Data.GetData(DataFormats.FileDrop); BitmapImage img = new BitmapImage(); using (Stream fileStream = fi[0].OpenRead()) { img.SetSource(fileStream); } ImgShow.Source = img; } }
We are done with the application. Run the application and drag and drop a file from Windows explorer to ImgShow image control.
Oval TextBox
My project require an oval text box. First place I started to make my changes is in the default template of Textbox. The default template of any control can be retrieved through expression blend (Right click on the Control and select Edit Template/Edit a Copy).
Below is the default template of TextBox control
<UserControl.Resources> <ControlTemplate x:Key="ValidationToolTipTemplate"> <Grid x:Name="Root" Margin="5,0" Opacity="0" RenderTransformOrigin="0,0"> <Grid.RenderTransform> <TranslateTransform x:Name="xform" X="-25"/> </Grid.RenderTransform> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="OpenStates"> <VisualStateGroup.Transitions> <VisualTransition GeneratedDuration="0"/> <VisualTransition GeneratedDuration="0:0:0.2" To="Open"> <Storyboard> <DoubleAnimation Duration="0:0:0.2" To="0" Storyboard.TargetProperty="X" Storyboard.TargetName="xform"> <DoubleAnimation.EasingFunction> <BackEase Amplitude=".3" EasingMode="EaseOut"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> <DoubleAnimation Duration="0:0:0.2" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/> </Storyboard> </VisualTransition> </VisualStateGroup.Transitions> <VisualState x:Name="Closed"> <Storyboard> <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/> </Storyboard> </VisualState> <VisualState x:Name="Open"> <Storyboard> <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="X" Storyboard.TargetName="xform"/> <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root"/> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Border Background="#052A2E31" CornerRadius="5" Margin="4,4,-4,-4"/> <Border Background="#152A2E31" CornerRadius="4" Margin="3,3,-3,-3"/> <Border Background="#252A2E31" CornerRadius="3" Margin="2,2,-2,-2"/> <Border Background="#352A2E31" CornerRadius="2" Margin="1,1,-1,-1"/> <Border Background="#FFDC000C" CornerRadius="2"/> <Border CornerRadius="2"> <TextBlock Foreground="White" MaxWidth="250" Margin="8,4,8,4" TextWrapping="Wrap" Text="{Binding (Validation.Errors)[0].ErrorContent}" UseLayoutRounding="false"/> </Border> </Grid> </ControlTemplate> <Style x:Key="OvalTextBoxStyle" TargetType="TextBox"> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Background" Value="#FFFFFFFF"/> <Setter Property="Foreground" Value="#FF000000"/> <Setter Property="Padding" Value="2"/> <Setter Property="BorderBrush"> <Setter.Value> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFA3AEB9" Offset="0"/> <GradientStop Color="#FF8399A9" Offset="0.375"/> <GradientStop Color="#FF718597" Offset="0.375"/> <GradientStop Color="#FF617584" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="TextBox"> <Grid x:Name="RootElement"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"/> <VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Duration="0" To="#FF99C1E2" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="MouseOverBorder"/> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/> </Storyboard> </VisualState> <VisualState x:Name="ReadOnly"> <Storyboard> <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ReadOnlyVisualElement"/> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="FocusStates"> <VisualState x:Name="Focused"> <Storyboard> <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisualElement"/> </Storyboard> </VisualState> <VisualState x:Name="Unfocused"> <Storyboard> <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisualElement"/> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="ValidationStates"> <VisualState x:Name="Valid"/> <VisualState x:Name="InvalidUnfocused"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="InvalidFocused"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ValidationErrorElement"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" Storyboard.TargetName="validationTooltip"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <System:Boolean>True</System:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Opacity="1" CornerRadius="20"> <Grid> <Border x:Name="ReadOnlyVisualElement" Background="#5EC9C9C9" Opacity="0" CornerRadius="20"/> <Border x:Name="MouseOverBorder" BorderBrush="Transparent" BorderThickness="1" CornerRadius="20"> <ScrollViewer x:Name="ContentElement" BorderThickness="0" IsTabStop="False" Padding="{TemplateBinding Padding}"/> </Border> </Grid> </Border> <Border x:Name="DisabledVisualElement" BorderBrush="#A5F7F7F7" BorderThickness="{TemplateBinding BorderThickness}" Background="#A5F7F7F7" IsHitTestVisible="False" Opacity="0" CornerRadius="20"/> <Border x:Name="FocusVisualElement" BorderBrush="#FF6DBDD1" BorderThickness="{TemplateBinding BorderThickness}" IsHitTestVisible="False" Margin="1" Opacity="0" CornerRadius="20"/> <Border x:Name="ValidationErrorElement" BorderBrush="#FFDB000C" BorderThickness="1" Visibility="Collapsed" CornerRadius="20"> <ToolTipService.ToolTip> <ToolTip x:Name="validationTooltip" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource ValidationToolTipTemplate}"> <ToolTip.Triggers> <EventTrigger RoutedEvent="Canvas.Loaded"> <BeginStoryboard> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" Storyboard.TargetName="validationTooltip"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <System:Boolean>true</System:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </ToolTip.Triggers> </ToolTip> </ToolTipService.ToolTip> <Grid Background="Transparent" HorizontalAlignment="Right" Height="12" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12"> <Path Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C" Margin="1,3,0,0"/> <Path Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff" Margin="1,3,0,0"/> </Grid> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources>
To make my TextBox oval I wanted to add/edit the CornerRadius property to some of the Border controls in the above Template. Below are the Borders we need to visit.
Border
DisabledVisualElement
FocusVisualElement
ReadOnlyVisualElement
MouseOverBorder
Add/Modify CornerRadius property to the above Borders and set it to what ever the value you need.
But the hurdle here is to change the CornerRadius, every time we need to set a different value to all the Border control I mentioned above. To make my life easier I created a new TextBox user control with CornerRadius property.
TextBox with CornerRadius property
Create a new UserControl and inherit from TextBox as shown below
<TextBox x:Class=”SonyControls.Controls.OvalTextBox”
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″
xmlns:System=”clr-namespace:System;assembly=mscorlib”
mc:Ignorable=”d”
d:DesignHeight=”300″d:DesignWidth=”400″>
<TextBox.Resources>
I am not adding the entire template here. Its already depicted above.
…..
….
<Border x:Name=”Border” BorderBrush=”{TemplateBinding BorderBrush}” BorderThickness=”{TemplateBinding BorderThickness}”
Background=”{TemplateBinding Background}” Opacity=”1″ CornerRadius=“{Binding CornerRadius}“>
</TextBox.Resources>
<TextBox.Style>
<StaticResource ResourceKey=”OvalTextBoxStyle”/>
</TextBox.Style
</TextBox>
Modify the CornerRadius value with “{Binding CornerRadius}” as shown above.
In the code behind of my user control I added a dependency property and some code as shown below.
public partial class OvalTextBox : TextBox { public OvalTextBox() { InitializeComponent(); } public static DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(OvalTextBox), new PropertyMetadata(null)); public CornerRadius CornerRadius { get { return (CornerRadius)GetValue(CornerRadiusProperty); } set { SetValue(CornerRadiusProperty, value); } } public override void OnApplyTemplate() { base.OnApplyTemplate(); SetDataContext();
}
private void SetDataContext()
{
string[] bordersToUpdate = { "Border", "DisabledVisualElement", "FocusVisualElement","ReadOnlyVisualElement", "MouseOverBorder", "WatermarkVisualElement" };
foreach (string str in bordersToUpdate)
{
Border border = GetTemplateChild(str) as Border;
if (border != null)
{ border.DataContext = this; }
}
}
}
That’s it we are done with a new Textbox with CornerRadius property, you can access the new property from Code behind or through XAML.
Silverlight UserControl Inheritance
My current project requires several silverlight user controls. Most of the control has some common functionalities. I decided to create a base class inherits from UserControl. Then add the base class to my user controls.
The issue arise when I tried to build the solution, I started getting the bellow compile time error.
Error 1 Partial declarations of ‘ImageEditor.Controls.CallOut’ must not specify different base classes F:\XXXX\obj\Debug\Controls\CallOut.g.i.cs 36 26 <ProjectName>
The solution to this issue is, in XAML change <UserControl …..</UserControl> to the name of your base class. See the eg. below
<UI:ControlBase x:Class="ImageEditor.Controls.CallOut" 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" xmlns:UI="clr-namespace:ImageEditor.Controls" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <Border x:Name="rectBorder" Grid.Row="0" BorderBrush="Black" BorderThickness="3" CornerRadius="5"> <TextBox x:Name="txtAnnotate" AcceptsReturn="True"></TextBox> </Border> </Grid> </UI:ControlBase >
In the above xaml I replace UserControl with my base class. In order to use my base class I should refer my namespace using xmlns:UI=”clr-…..”. You can see the changes by looking at the sections underlined.
Image Positioning in Silverlight
The below code will give you a basic idea of how dragging and positioning an image inside a Canvas will work.
XAML code
<UserControl x:Class=”EmblemDesigner.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”
d:DesignHeight=”300″ d:DesignWidth=”400″>
<Grid x:Name=”LayoutRoot” Background=”White”>
<Canvas x:Name=”emblemDesign” AllowDrop=”True” Background=”Black” MouseLeftButtonUp=”emblemDesign_MouseLeftButtonUp” MouseMove=”imgEmblem_MouseMove”>
<Image x:Name=”imgEmblem” Source=”/Images/One.jpg” Height=”100″ Width=”100″ MouseLeftButtonDown=”imgEmblem_MouseLeftButtonDown” MouseLeftButtonUp=”imgEmblem_MouseLeftButtonUp”></Image>
</Canvas>
</Grid>
</UserControl>
Code Behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace EmblemDesigner
{
public partial class MainPage : UserControl
{
double _imgPosX = 0;
double _imgPosY = 0;
double _mousePosX = 0;
double _mousePosY = 0;
bool _isLeftMouseDown = false;
public MainPage()
{
InitializeComponent();
}
private void imgEmblem_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_isLeftMouseDown = true;
_imgPosX = imgEmblem.Margin.Top;
_imgPosY = imgEmblem.Margin.Left;
_mousePosX = e.GetPosition(imgEmblem).X;
_mousePosY = e.GetPosition(imgEmblem).Y;
imgEmblem.Cursor = Cursors.Hand;
}
private void imgEmblem_MouseMove(object sender, MouseEventArgs e)
{
if (_isLeftMouseDown == false)
return;
TranslateTransform tt = new TranslateTransform();
if (_imgPosX != -1)
{
tt.X = _imgPosX + (e.GetPosition(emblemDesign).X – _mousePosX);
}
if (_imgPosY != -1)
{
tt.Y = _imgPosY + (e.GetPosition(emblemDesign).Y – _mousePosY);
}
imgEmblem.RenderTransform = tt;
resizeCanvas.Margin = imgEmblem.Margin;
System.Diagnostics.Debug.WriteLine(“X={0} y={1}”, tt.X, tt.Y);
}
private void imgEmblem_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_isLeftMouseDown = false;
imgEmblem.Cursor = Cursors.Arrow;
}
private void emblemDesign_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
//_isLeftMouseDown = false;
}
}
}
Silverlight Developer runtime not installed Error
I started getting the error “Unable to start debugging. The Silverlight Developer Runtime is not installed. Please install a matching version.”. The error started poping after I installed Silverlight 4.0 tools. The resolution to this problem is to install Silverlight_Developer.exe. You can download the setup from http://go.microsoft.com/fwlink/?LinkID=188039 .
Thanks to the post http://forums.silverlight.net/forums/p/176999/421658.aspx
