Thursday, February 10, 2011

Combo Box drop down on Mouse Leave/ Mouse Enter in XAML

Want your combobox to drop when you hover on it? And hide items when leave combobox? You are at the right place to find the answer.

Download sample project here.

Achieving this is not straight forward. You need to do some tweaking with the Combox style itself. In Expression Blend create a copy of the combobox template and rename it to comboboxstyle

image

You see a template structure created. Below are the parts.

image

So, you need On MainGrid MouseEnter = Popup.Open; On MainGrid.MouseLeave = Popup.Close. Below is the code which does that.

<Grid x:Name="MainGrid" SnapsToDevicePixels="true">
<
Grid.ColumnDefinitions
>
<
ColumnDefinition Width
="*"/>
<
ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width
="0"/>
</
Grid.ColumnDefinitions
>
<
i:Interaction.Triggers>
<
i:EventTrigger EventName
="MouseEnter">
<
ei:ChangePropertyAction TargetObject="{Binding ElementName=PART_Popup}" PropertyName="IsOpen" Value
="True"/>
</
i:EventTrigger
>
<
i:EventTrigger EventName
="MouseLeave">
<
ei:ChangePropertyAction TargetObject="{Binding ElementName=PART_Popup}" PropertyName="IsOpen" Value
="False"/>
</
i:EventTrigger
>
</
i:Interaction.Triggers
>
<
Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<
Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName
=MainGrid}">
<
Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors
.WindowBrushKey}}">
<
ScrollViewer x:Name
="DropDownScrollViewer">
<
Grid RenderOptions.ClearTypeHint
="Enabled">
<
Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width
="0">
<
Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName
=DropDownBorder}"/>
</
Canvas
>
<
ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels
}"/>
</
Grid
>
</
ScrollViewer
>
</
Border
>
</
Microsoft_Windows_Themes:SystemDropShadowChrome
>
</
Popup
>
<
ToggleButton BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxReadonlyToggleButton
}"/>
<
ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment
}"/>
</
Grid
>

Enumeration (Enum) in XAML

This post describes how to use C# Enumerations in XAML. There are multiple usages exposing enumerations in XAML.

Download sample application here.

Declare the enumeration outside a class in a namespace XXX.

namespace ApplicationWithEnumeration
{
/// <summary>
///
Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
}

public enum Person
{
None1,
Patient1,
Employee1,
Doctor1,
Nurse1
}
}
Use the namespace in the XAML side.
xmlns:local="clr-namespace:ApplicationWithEnumeration"
- OR -
xmlns:XXX="clr-namespace:****"
Done! now you can use it with below declaration.
{x:Static local:Person.Patient1}"

Example,

<ComboBoxItem Content="Patient" Style="{DynamicResource SimpleComboBoxItem}" Tag="{x:Static local:Person.Patient1}" />
- OR - 
<Label Content="{x:Static local:Person.Patient1}" Margin="52.2,0,68.2,46.04" Style="{DynamicResource SimpleLabel}" VerticalAlignment="Bottom" Background="#006F5A5A"/>

Similarly, you can use it iin ChangePropertyAction as parameter dependency and InvokeCommandAction as CommandParamters.


Folks, if you are aware of other usages, please post in the Comments section. I can append to the post later.

Sunday, February 6, 2011

Using ViewModel in Views (The MVVM way)

The article says how to use one/more ViewMiodels in a View. Also, multiple Views using same ViewModel. Read this post to create a ViewModel.

There are several ways of using ViewModel in a XAML file based on the different scenarios.

Case 1: Initialize a ViewModel without any constructor parameters.

Read this post. This article also expects the reader to be familiar with Expression Blend.

Case 2: Initialize a ViewModel with constructor parameters.

xmlns:local="clr-namespace:****your namespace***"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
<ObjectDataProvider ObjectType="{x:Type local:ViewModel}" x:Key="theViewModel">            <ObjectDataProvider.ConstructorParameters>
<
sys:String>Uday</sys:String>
<
sys:Int32>25</sys:Int32>
</
ObjectDataProvider.ConstructorParameters>
</
ObjectDataProvider>

Notice the System namespace declaration. We can use any data type from System namespace as a constructor parameter. If you have custom data types you can pass them too as a constructor parameter; by declaring namespace in XAML file.


Case 3: In the above two cases, the object can be accessed on the C# side (as a Resource). However, if you update the object in C# side, the ViewModel values are NOT reflected on the XAML side. Now what??!  We can declare a ViewModel object on the C# side and declare it as DataContext for the XAML side. Here is how to do it.

public partial class MainWindow : Window
{
private ViewModel theViewModel = new ViewModel();

public MainWindow()
{
InitializeComponent();
this.DataContext = theViewModel;
}

private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
theViewModel.FirstName = "I am still typing";
}
}

In this way, any updates from XAML side would be seen on C# side too. But, if you use Expression Blend, the ViewModel is not seen in the designer, so you have to write the binding code manually. I wouldn’t suggest this way, but sometime its not up to your choice!

Saturday, February 5, 2011

View Model data binding to the View(XAML) using Expression Blend 4

This tutorial uses Expression Blend 4. Refer to my previous post for creating ViewModel and data binding.

In E Blend, create a link for the viewModel structure as below:

image

Edit a name for the DataSource; rename it ‘theViewModel’

SNAGHTML27cf514

After the adding the data source, you can see theViewModel section under ‘Data’. All the properties and methods are exposed in expression blend.

image


Create some UI elements on the View.

image

Ready to create some bindings??

image

image

image

Done?!  Did you notice the value is seen under the Text property immediately . All data bound UI elements show values at design time.

image

Binding with the variables is simple; however calling ViewModel’s method on button click (or on other events) needs some additional work. From behaviors section, drag ‘CallMethodAction’ and drog on the ‘Update Info’ Button.

image

This action adds references to ‘Microsoft.Expression.Interactions.dll’ This action will create a CallMethodAction as child to the button. Edit its Properties as shown below.

image

DONE! now, when you click the button, the Update Method is called. Editing the textboxes will update the view model variables instantaneously.

Download sample application here. Here is the code for it:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ViewModelDatabinding" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" mc:Ignorable="d" x:Class="ViewModelDatabinding.MainWindow"
Title="MainWindow" Height="350" Width="525">
<
Window.Resources>
<
local:ViewModel x:Key="theViewModel" d:IsDataSource="True"/>
</
Window.Resources>
<
Grid DataContext="{Binding Source={StaticResource theViewModel}}">
<
TextBox HorizontalAlignment="Left" Margin="42.4,43.2,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="153.953" Text="{Binding FirstName}"/>
<
TextBox HorizontalAlignment="Left" Margin="42.4,92,0,0" TextWrapping="Wrap" Text="{Binding LastName}" VerticalAlignment="Top" Width="153.953"/>
<
Slider x:Name="slider" HorizontalAlignment="Left" Margin="42.4,148,0,144" d:LayoutOverrides="Height" Width="153.953" Maximum="100" Minimum="1" SmallChange="1" IsSnapToTickEnabled="True" Value="{Binding Age}"/>
<
Label Content="{Binding Value, ElementName=slider}" HorizontalAlignment="Left" Margin="213.6,148,0,140.04" d:LayoutOverrides="Height"/>
<
Button Content="Update Info" HorizontalAlignment="Left" Margin="58.4,0,0,32.04" VerticalAlignment="Bottom" Width="95.8" Height="78.76">
<
i:Interaction.Triggers>
<
i:EventTrigger EventName="Click">
<
ei:CallMethodAction TargetObject="{Binding Mode=OneWay}" MethodName="Update"/>
</
i:EventTrigger>
</
i:Interaction.Triggers>
</
Button>
</
Grid>
</
Window>

View Model 101

A View Model(VM) is a .cs file containing all the data required for the XAML file. It is like a code behind for a XAML file which holds all the data logic, data variables and methods required for the XAML UI layer(also referred as ‘View’) to show data.

Then, what's in the MainWindow.cs??

If you have a perfect VM design, thenMainWindow.cs should have ONLY the constructor with InitializeComponent() method. But, we cant achieve this most of the time. It often includes some complex event handler call back functions.

Here are some examples';

To achieve MainWindow.cs VM
On selecting a record from a datagrid full of patients; should show a Message Box with patient Info. On RecodActivated call back method with Message Box functionality. A datatable containing all the patient info.
On a button.click event, load information from a file and also enable the associated datagrid Enable the datagrid A method to load information from file.

Most of the event handling can be done through XAML; Expression Blend 4 is mature enough to enable/disable/converter values using data binding.


Before we start, read about Properties and INotifyPropertyChanged.

ONLY the public Properties and public methods of VM are accessible by the View/XAML file. You can create Properties with code snippets prop or propfull

Sample VM,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace ViewModelDatabinding
{
public class ViewModel : INotifyPropertyChanged
{

#region Property Changed Event

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}

#endregion

private string
firstName;
public string FirstName
{
get { return firstName; }
set { firstName = value; OnPropertyChanged("FirstName"); }
}

private string lastName;
public string LastName
{
get { return lastName; }
set { lastName = value; OnPropertyChanged("LastName");}
}

private double age;
public double Age
{
get { return age; }
set { age = value; }
}

public ViewModel()
{
FirstName = "Uday";
LastName = "Thummalapalli";
Age = 25;
}

public void Update()
{
FirstName = "Dr. Uday";
LastName = "Genius";
Age = 29;
}

}
}

What is Data Binding in WPF? how is it used?

In Windows Forms, developer creates the UI and updates the UI elements’ content and states based on the certain conditions. For example, in the designer we create a textbox  and write a logic to update the content of the textbox.

image

For the button1 and button2 Click event; we write a call  back function.

private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = "User Uday";
        }

        private void button2_Click(object sender, EventArgs e)
        {
            textBox1.Text = "User Kiran";
        }

Now, when we click button1; textbox1 changes to “User Uday” and “User Kiran”.

In a real world situation, we have to execute the code

textBox1.Text = "User ****";

when the a user is selected from a huge list. Imagine 50 such textboxes and 100 labels and update logic in the code behind. so many lines of code, isn't it?


WPF allows data binding where the textbox1 keeps monitoring on a particular variable/string. Whenever the value of the variable/string updates; the textbox1.Text also updates. Now, wouldn’t that be nice that all the elements in the UI update keeps monitoring(binding) on a set of variables?

Here is an example. draw textBox using designer. Click here to download the sample applicatiion.

image

Write the data binding for the text property to the Windows width property in xaml,

<Window x:Name="window" x:Class="SampleTextBoxBinding.MainWindow"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Sample TextBox Binding" Height="188" Width="273">
    <Grid>
        <TextBox Height="23" HorizontalAlignment="Left" Margin="62,65,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Text="{Binding Width, ElementName=window}" />
    </Grid>
</Window>

Now, if the increase the window size the value updates during runtime;

SNAGHTML2a3294

GREAT!

Now, how cam I do this functionality to make an application? Click here for sample application

SNAGHTML6c0178

Below is the code for it.

<Window x:Class="SalutaionName.MainWindow"
        xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="DataBinding Example" Height="256.4" Width="633.8">
    <Grid>
        <CheckBox x:Name="checkBox" Content="Salutation" HorizontalAlignment="Left" Margin="13.4,46.6,0,0" VerticalAlignment="Top" FontFamily="Times New Roman" FontSize="16"/>
        <CheckBox x:Name="checkBox1" Content="First Name" HorizontalAlignment="Left" Margin="13.4,78.6,0,0" VerticalAlignment="Top" FontFamily="Times New Roman" FontSize="16"/>
        <CheckBox x:Name="checkBox2" Content="Last Name" HorizontalAlignment="Left" Margin="13.4,110.6,0,0" VerticalAlignment="Top" FontFamily="Times New Roman" FontSize="16"/>
        <TextBox x:Name="textBox" Margin="115.8,78.6,8,0" TextWrapping="Wrap" VerticalAlignment="Top" IsEnabled="{Binding IsChecked, ElementName=checkBox1}" FontFamily="Times New Roman" FontSize="16" Text="FName"/>
        <TextBox x:Name="textBox1" Margin="115.8,107.6,8,0" TextWrapping="Wrap" VerticalAlignment="Top" IsEnabled="{Binding IsChecked, ElementName=checkBox2}" FontFamily="Times New Roman" FontSize="16" Text="LName"/>
        <ComboBox x:Name="comboBox" Margin="115.8,46.6,8,0" VerticalAlignment="Top" IsEnabled="{Binding IsChecked, ElementName=checkBox}" FontFamily="Times New Roman" FontSize="16" SelectedIndex="0">
            <sys:String>Mr</sys:String>
            <sys:String>Dr</sys:String>
            <sys:String>Majesty</sys:String>
            <sys:String>Highness</sys:String>           
        </ComboBox>
        <TextBlock Margin="13.4,157,8,8" TextWrapping="Wrap" FontFamily="Times New Roman" FontSize="16">
        <TextBlock.Text>
            <MultiBinding StringFormat="{}{0}. {1} {2}">
                  <Binding ElementName="comboBox" Path="SelectedValue" Mode="OneWay"/>
                <Binding ElementName="textBox" Path="Text" Mode="OneWay"/>
                <Binding ElementName="textBox1" Path="Text" Mode="OneWay"/>
    </MultiBinding>
  </TextBlock.Text>
        
        </TextBlock>
       
    </Grid>
</Window>