Android:Update UI through handler

August 22, 2012 1 comment

Here is a short memo for Handler class I have used in my recent project.

Class Overview

A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Scheduling messages is accomplished with the post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler’s handleMessage(Message) method (requiring that you implement a subclass of Handler).

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler’s message queue and processed when appropriate.

A Handler can send both Message and Runable objects.

Layout


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<TextView
android:id="@+id/txtView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_weight="1"
android:background="#969696"
android:textColor="#fff"
android:textSize="29sp" />

<Button
android:id="@+id/btnTest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Handle in Android" />

</LinearLayout>

  • Message

public class MainActivity extends Activity {

private int index = 0;

private Button btnTest;
private TextView txtView;

private final Handler handler = new Handler() {
public void handleMessage(Message msg) {

txtView.setText(index++ + "");
}
};

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

txtView = (TextView) findViewById(R.id.txtView);
txtView.setText(index++ + "");

btnTest = (Button) findViewById(R.id.btnTest);
btnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
public void run() {
try {
Thread.sleep(1000);

Message msg = handler.obtainMessage();
handler.sendMessage(msg);

} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
});
}
}

  • Runable

public class MainActivity extends Activity {

private int index = 0;

private Button btnTest;
private TextView txtView;

private final Handler handler = new Handler();

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

txtView = (TextView) findViewById(R.id.txtView);
txtView.setText(index++ + "");

btnTest = (Button) findViewById(R.id.btnTest);
btnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.post(new Runnable() {

@Override
public void run() {

txtView.setText(index++ + "");
}
});
}
});
}
}

Having the Handler final and declaring it in the class definition is a good practice.

A sample for progress bar , using both runnable and message.

Layout


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:progress="0"
android:visibility="gone" />

<Button
android:id="@+id/btnTest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Handle in Android" />

</LinearLayout>

Java


public class MainActivity extends Activity {

private Button btnTest;
ProgressBar progressBar;

private final Handler handler = new Handler() {

@Override
public void handleMessage(Message msg) {
progressBar.setProgress(msg.arg1);
handler.postDelayed(run, 1000);
}
};

private final Runnable run = new Runnable() {

int index = 0;

@Override
public void run() {

index += 10;
Message msg = handler.obtainMessage();
msg.arg1 = index;

handler.sendMessage(msg);
if (index >= 100)
handler.removeCallbacks(this);
}
};

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

progressBar = (ProgressBar) findViewById(R.id.progressBar);

btnTest = (Button) findViewById(R.id.btnTest);
btnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
progressBar.setProgress(0);
progressBar.setVisibility(View.VISIBLE);
handler.post(run);
}
});
}
}

Categories: Android

Android:Make all buttons with the same width inside linearlayout.

August 16, 2012 Leave a comment

I need to create a Tab like UI at the bottom of  main Layout today.

But for some reason , I can’t use TabHost control .  So I have to put all buttons inside a LinearLayout with android:layout_alignParentBottom=”true”   . Source code is like below .


<LinearLayout
android:id="@+id/linearLayoutBottom"
android:layout_width="fill_parent"
android:layout_height="48dp"
android:background="@color/black"
android:gravity="center"
android:layout_alignParentBottom="true">

<Button
android:id="@+id/btn1"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:gravity="center"
android:text="@string/button1"
style="@style/TabStyle"
android:background="@drawable/style_button_black"
android:textColor="@color/snowblack"/>

<Button

android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:gravity="center"
android:text="@string/button2"
style="@style/TabStyle"
android:background="@drawable/style_button_snowblack"
android:textColor="@color/white"/>

<Button
                        android:layout_weight="1"
android:layout_width="0dp"
                        android:layout_height="fill_parent"
android:gravity="center"
android:text="@string/button3"
style="@style/TabStyle"
android:background="@drawable/style_button_black"
android:textColor="@color/snowblack"/>

</LinearLayout>

 

The final effect :

Memo:

I just need use android:layout_width=”0dp” and android:layout_weight=”1″ on these buttons 

Setting the ‘width’ to be “0” means you don’t want to explicitly enforce a width and that the OS should work out the relative widths as fractions of total weight. 

Setting the ‘weight‘ to be “1” means , 1+1+1 = 3 (total weight). So each of them will take 1/3 total width of the screen width.

Categories: Android

WPF : Applying Data Templates Dynamically in ContentControl

July 6, 2012 3 comments

Yesterday , I have post an article about Applying Data Templates Dynamically in ItemsControl . In that article , I have loaded all the data at first , and render the view at once time.

But sometimes I would also like to load data dynamically . As a sample like bellow.

When Q1 be selected in left list , Single Choice View will be loaded in right ContentControl.

When Q2 be selected in left list , Multiple Choice View will be loaded in right ContentControl.

When Q3 be selected in left list , FreeText View will be loaded in right ContentControl.

Implementation

The Data Structure is almost like yesterday.

First , I have designed a data structure for each type of question view model.

  • interface
public interface IQuestion
{
string Title { get; set; }
void GetContent();
}

Each type of question(View Model)

  • Single Choice ViewModel
public class SingleViewModel : IQuestion
{
public string Title { get; set; }

public ObservableCollection<Category> Categories { get; set; }

public SingleViewModel()
{
Categories = new ObservableCollection<Category>();
}

void IQuestion.GetContent()
{
Categories.Clear();
Categories.Add(new Category() { Title = "Test1" });
Categories.Add(new Category() { Title = "Test2" });
Categories.Add(new Category() { Title = "Test3" });
Categories.Add(new Category() { Title = "Test4" });
Categories.Add(new Category() { Title = "Test5" });
}
}
  • Multiple Choice ViewModel
public class MultipleViewModel : IQuestion
{
public string Title { get; set; }
public ObservableCollection<Category> Categories { get; set; }

public MultipleViewModel()
{
Categories = new ObservableCollection<Category>();
}

void IQuestion.GetContent()
{
Categories.Clear();
Categories.Add(new Category() { Title = "Test1" });
Categories.Add(new Category() { Title = "Test2" });
Categories.Add(new Category() { Title = "Test3" });
Categories.Add(new Category() { Title = "Test4" });
Categories.Add(new Category() { Title = "Test5" });
}
}
  • Free Text ViewModel
public class FreeTextViewModel : IQuestion
{
public string Title { get; set; }
public string Content { get; set; }

public FreeTextViewModel()
{
}

void IQuestion.GetContent()
{
Content = "dummy data for free text";
}
}

  • Defined a viewmodel for main page
Here is different with yesterday . 
I have defined a ICommand to invoke the LoadContent method dynamically when selectionchanged event be raised.
public class Sample6ViewModel : ViewModelBase
{
public ObservableCollection<IQuestion> Questions { get; set; }
public IQuestion SelectedQuestion { get; set; }
public ICommand LoadContent { get; set; }

public Sample6ViewModel()
{
Questions = new ObservableCollection<IQuestion>();
Questions.Add(new SingleViewModel() { Title="Q1 : Single Choice Sample"});
Questions.Add(new MultipleViewModel() { Title = "Q2 : Multiple Choice Sample" });
Questions.Add(new FreeTextViewModel() { Title = "Q3 : FreeText Sample" });

SelectedQuestion = Questions[0];
SelectedQuestion.GetContent();

LoadContent = new RelayCommand(
() =>
{
SelectedQuestion.GetContent();
}, () => {
return SelectedQuestion != null;
});
}
}

View

  • Define each type of questions's  DataTemplate
I have created three UserControls to placing these three types for making main view source more clearly.

SingleChoiceView.xaml

<UserControl x:Class="MVVMSample1.View.SingleChoiceView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Width="auto">
<TextBlock Text="{Binding Title}" HorizontalAlignment="Left" FontWeight="12" Margin="2,0,0,2"/>

<ItemsControl ItemsSource="{Binding Categories}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="RadioName" Content="{Binding Title}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</UserControl>

MultipleChoiceView.xaml

<UserControl x:Class="MVVMSample1.View.MultipleChoiceView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Width="auto">
<TextBlock Text="{Binding Title}" HorizontalAlignment="Left" FontWeight="12" Margin="2,0,0,2"/>

<ItemsControl ItemsSource="{Binding Categories}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Title}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</UserControl>

FreeTextView.xaml

<UserControl x:Class="MVVMSample1.View.FreeTextView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Width="auto">
<TextBlock Text="{Binding Title}" HorizontalAlignment="Left" FontWeight="12" Margin="2,0,0,2"/>
<TextBlock Text="{Binding Content}" Margin="5,5,0,15" HorizontalAlignment="Left" TextWrapping="Wrap" />
</StackPanel>
</UserControl>
  • Define MainView
<Window.DataContext>
<vm:Sample6ViewModel />
</Window.DataContext>

<Window.Resources>
<System:String x:Key="PageTitle">WPF Simplified - Part6</System:String>

<DataTemplate x:Key="QuestionListTemplate">
<StackPanel Orientation="Horizontal" Width="auto">
<TextBlock Text="{Binding Title}" HorizontalAlignment="Left" Margin="2,0,0,2"/>
</StackPanel>
</DataTemplate>

<DataTemplate DataType="{x:Type vm:SingleViewModel}">
<vw:SingleChoiceView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MultipleViewModel}">
<vw:MultipleChoiceView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:FreeTextViewModel}">
<vw:FreeTextView />
</DataTemplate>
</Window.Resources>

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<ListBox x:Name="lstFriend" Grid.Column="0"
ItemsSource="{Binding Questions}"
SelectedItem="{Binding SelectedQuestion}"
ItemTemplate="{StaticResource QuestionListTemplate}"
cmd:CommandBehavior.RoutedEventName="SelectionChanged"
cmd:CommandBehavior.TheCommandToRun="{Binding Path=LoadContent}"/>

<GridSplitter  Grid.Column="1" ResizeBehavior="PreviousAndNext" Background ="BlueViolet"  Width="1" />

<ContentControl Grid.Column="2"
Content="{Binding SelectedItem , ElementName=lstFriend}">

</ContentControl>
</Grid>

■ Download
WPF : Applying Data Templates Dynamically in ContentControl

Categories: Article, WPF

WPF : Applying Data Templates Dynamically in ItemsControl

July 5, 2012 1 comment

I want to create a survey form by WPF like bellow.

As we known there are many types in a questionnaire sheet.
Single Choice , Mutiple Choice ,Free Text and so on .
So , I would like to find a way to create the entire sheet with full of types and least code.

Fortunately there are also many cool features in WPF to help me implement it very easy.

First , I have designed a data structure for each type of question view model.

  • interface
public interface IQuestion
{
string Title { get; set; }
void GetContent();
}

Each type of question(View Model)

  • Single Choice ViewModel
public class SingleViewModel : IQuestion
{
public string Title { get; set; }

public ObservableCollection<Category> Categories { get; set; }

public SingleViewModel()
{
Categories = new ObservableCollection<Category>();
}

void IQuestion.GetContent()
{
Categories.Add(new Category() { Title = "Test1" });
Categories.Add(new Category() { Title = "Test2" });
Categories.Add(new Category() { Title = "Test3" });
Categories.Add(new Category() { Title = "Test4" });
Categories.Add(new Category() { Title = "Test5" });
}
}
  • Multiple Choice ViewModel
public class MultipleViewModel : IQuestion
{
public string Title { get; set; }
public ObservableCollection<Category> Categories { get; set; }

public MultipleViewModel()
{
Categories = new ObservableCollection<Category>();
}

void IQuestion.GetContent()
{
Categories.Add(new Category() { Title = "Test1" });
Categories.Add(new Category() { Title = "Test2" });
Categories.Add(new Category() { Title = "Test3" });
Categories.Add(new Category() { Title = "Test4" });
Categories.Add(new Category() { Title = "Test5" });
}
}
  • Free Text ViewModel
public class FreeTextViewModel : IQuestion
{
public string Title { get; set; }
public string Content { get; set; }

public FreeTextViewModel()
{
}

void IQuestion.GetContent()
{
Content = "dummy data for free text";
}
}

  • Defined a viewmodel for main page
public class Sample5ViewModel : ViewModelBase
{
public ObservableCollection<IQuestion> Questions { get; set; }

public Sample5ViewModel()
{
Questions = new ObservableCollection<IQuestion>();
Questions.Add(new SingleViewModel() { Title="Q1 : Single Choice Sample"});
Questions.Add(new MultipleViewModel() { Title = "Q2 : Multiple Choice Sample" });
Questions.Add(new FreeTextViewModel() { Title = "Q3 : FreeText Sample" });

foreach (var q in Questions)
q.GetContent();
}
}

View

  • Define the DataContext to Bind ItemsControl

<Window.DataContext>
<vm:Sample5ViewModel />
</Window.DataContext>
<Window.Resources>

  • Define each type of questions’s  DataTemplate
<DataTemplate DataType="{x:Type vm:SingleViewModel}">
<StackPanel Orientation="Vertical" Width="auto">
<TextBlock Text="{Binding Title}" HorizontalAlignment="Left" FontWeight="12" Margin="2,0,0,2"/>
<ItemsControl ItemsSource="{Binding Categories}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="RadioName" Content="{Binding Title}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>

<DataTemplate DataType="{x:Type vm:MultipleViewModel}">
<StackPanel Orientation="Vertical" Width="auto">
<TextBlock Text="{Binding Title}" HorizontalAlignment="Left" FontWeight="12" Margin="2,0,0,2"/>
<ItemsControl ItemsSource="{Binding Categories}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Title}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>

<DataTemplate DataType="{x:Type vm:FreeTextViewModel}">
<StackPanel Orientation="Vertical" Width="auto">
<TextBlock Text="{Binding Title}" HorizontalAlignment="Left" FontWeight="12" Margin="2,0,0,2"/>
<TextBlock Text="{Binding Content}" Margin="5,5,0,15" HorizontalAlignment="Left" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
  • Define ItemsControl
<ItemsControl x:Name="lstQuestion"  ItemsSource="{Binding Questions}" />
Categories: Article, WPF

Overview of WPF Layouts

  • Canvas

A Canvas panel is used to position child elements by using explicit coordinates.
The coordinates can be specified relative to any side of the panel by using Canvas.Left, Canvas.Top, Canvas.Bottom and Canvas.Right  properties.

A Canvas has default Height and Width properties of zero


<Canvas>
<Canvas Background="Bisque" Height="200" Width="200">
<TextBlock Foreground="Blue" Text="Default Height &amp; Width are zero"></TextBlock>
</Canvas>
</Canvas>

Set Canvas.Left and Canvas.Top to move Canvas


<Canvas>
<Canvas Canvas.Top="50" Canvas.Left="50" Background="Bisque" Height="200" Width="200">
<TextBlock Foreground="Blue" Text="Default Height &amp; Width are zero"></TextBlock>
</Canvas>
</Canvas>

Resize the Canvas and will not clip the content when ClipToBounds is false(Default value)


<Canvas>
<Canvas Background="Bisque" Height="150" Width="150">
<TextBlock Foreground="Blue" Text="Default Height &amp; Width are zero"></TextBlock>
</Canvas>
</Canvas>

Resize the Canvas and will clip the content when ClipToBounds is true


<Canvas>
<Canvas Background="Bisque" Height="150" Width="150"  ClipToBounds="True">
<TextBlock Foreground="Blue" Text="Default Height &amp; Width are zero"></TextBlock>
</Canvas>
</Canvas>

  • StackPanel

A StackPanel allows you to stack elements in a specified direction. By using properties that are defined on StackPanel, content can flow both vertically, which is the default setting, or horizontally.

Orientation=”Vertical”(Default value)

<StackPanel>
<Button Content="Button1"/>
<Button Content="Button2" Height="30" Width="100" />
<Button Content="Button3"/>
</StackPanel>

Orientation=”Horizontal”

<StackPanel Orientation="Horizontal">
<Button Content="Button1"/>
<Button Content="Button2" Height="30" Width="100" />
<Button Content="Button3"/>
</StackPanel>

Add a ScrollBar to a Stackpanel


<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel />
</ScrollViewer>

Padding on StackPanel


<Border Padding="10">
<StackPanel />
</Border>
  • WrapPanel

The WrapPanel is similar to the StackPanel but it does not just stack all child elements to one row, it wraps them to new lines if no space is left. The Orientation can be set to Horizontal or Vertical.

Orientation=”Horizontal”


<WrapPanel Orientation="Horizontal" Width="160" >
<Button Content="Button1" />
<Button Content="Button2" />
<Button Content="Button3" />
<Button Content="Button4" />
<Button Content="Button5" />
</WrapPanel>

Orientation=”Vertical”


<WrapPanel Orientation="Vertical" Height="80" Background="Aquamarine">
<Button Content="Button1" />
<Button Content="Button2" />
<Button Content="Button3" />
<Button Content="Button4" />
<Button Content="Button5" />
</WrapPanel>

  • DockPanel

The dock panel is a layout panel, that provides an easy docking of elements to the left, right, top, bottom or center of the panel. The dock side of an element is defined by the attached property DockPanel.Dock. To dock an element to the center of the panel, it must be the last child of the panel and the LastChildFill property must be set to true.

Set the LastChildFill property to true(default value) 


<DockPanel>
<Button Content="Dock=Top" DockPanel.Dock="Top"/>
<Button Content="Dock=Bottom" DockPanel.Dock="Bottom"/>
<Button Content="Dock=Left"/>
<Button Content="Dock=Right" DockPanel.Dock="Right"/>
<Button Content="LastChildFill=True"/>
</DockPanel>

Set the LastChildFill property to false


<DockPanel LastChildFill="False">
<Button Content="Dock=Top" DockPanel.Dock="Top"/>
<Button Content="Dock=Bottom" DockPanel.Dock="Bottom"/>
<Button Content="Dock=Left"/>
<Button Content="Dock=Right" DockPanel.Dock="Right"/>
<Button Content="LastChildFill=False" DockPanel.Dock="Top"/>
</DockPanel>

  • Grid

The grid is a layout panel that arranges its child controls in a tabular structure of rows and columns. Its functionality is similar to the HTML table but more flexible. A cell can contain multiple controls, they can span over multiple cells and even overlap themselves.

Create a simple Grid with controls.


<Grid>
 <Grid.RowDefinitions>
 <RowDefinition Height="Auto" />
 <RowDefinition Height="*" />
 <RowDefinition Height="80" />
 </Grid.RowDefinitions>
 <Grid.ColumnDefinitions>
 <ColumnDefinition Width="Auto" />
 <ColumnDefinition Width="*" />
 <ColumnDefinition Width="80" />
 </Grid.ColumnDefinitions>

<Button Grid.Column="0" Grid.Row="0" Content="Cells(0,0)"  />
 <Button Grid.Column="0" Grid.Row="1" Content="Cells(1,0)"  />
 <Button Grid.Column="0" Grid.Row="2" Content="Cells(2,0)"  />
 <Button Grid.Column="1" Grid.Row="0" Content="Cells(0,1)"  />
 <Button Grid.Column="1" Grid.Row="1" Content="Cells(1,1)"  />
 <Button Grid.Column="1" Grid.Row="2" Content="Cells(2,0)"  />
 <Button Grid.Column="2" Grid.Row="0" Content="Cells(0,2)"  />
 <Button Grid.Column="2" Grid.Row="1" Content="Cells(1,2)"  />
 <Button Grid.Column="2" Grid.Row="2" Content="Cells(2,2)"  />
</Grid>

Set up a WPF Gridsplitter to resize two columns


<Grid>
 <Grid.ColumnDefinitions>
 <ColumnDefinition Width="*"/>
 <ColumnDefinition Width="Auto"/>
 <ColumnDefinition Width="*"/>
 </Grid.ColumnDefinitions>

<Canvas Background="AliceBlue" Grid.Column="0"></Canvas>
 <GridSplitter  Grid.Column="1" ResizeBehavior="PreviousAndNext" Background ="BlueViolet"  Width="1" />
 <Canvas Background="Aquamarine" Grid.Column="2"></Canvas>
 </Grid>

Categories: Article, WPF

MVVM Pattern Simplified

Here is an article about WPF Apps With The Model-View-ViewModel Design Pattern from MSDN

MVVM pattern splits your code into 3 parts – Model, View and ViewModel .

We can experience many features from MVVM pattern .

As my own feelings are bellow

  • Make your code testable (UT)
  • Work together with designers (who use Blend) easily.
  • Move the bussiness logic code away from your view .
  • More flexibility to change your UI without having to write other logic in the code.

Here is a simple way for the communication with View and ViewModel

  • Model

public class Friend
{
public string Name { get; set; }
public string Description { get; set; }
public string Image { get; set; }
}

  • ViewModel

public class FriendViewModel : ViewModelBase
{
public ObservableCollection<Friend> Friends {get;set;}

public FriendViewModel()
{
Friends = new ObservableCollection<Friend>();
Friends.Add(new Friend() { Name = "Shen", Description = "This is a sample1", Image = "http://goo.gl/I3lmQ" });
Friends.Add(new Friend() { Name = "Ricardo", Description = "This is a sample2", Image = "http://goo.gl/fAmHb" });
}
}

  • View

<Window x:Class="MVVMSample1.View.Sample2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:MVVMSample1.Model"
xmlns:vm="clr-namespace:MVVMSample1.ViewModel"
Title="WPF Samples" Height="380" Width="280">

<Window.DataContext>
<vm:FriendViewModel />
</Window.DataContext>

<Window.Resources>

<System:String x:Key="PageTitle">WPF Simplified - Part1</System:String>

<DataTemplate x:Key="FriendItemTemplate">
<StackPanel Orientation="Horizontal" Width="223" Margin="2,5,5,2"  Background="Black" >
<Image Source="{Binding Image}" Height="64" Width="64" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2"/>
<StackPanel Orientation="Vertical" Width="auto">
<TextBlock Text="{Binding Name}" HorizontalAlignment="Left" Foreground="#FFF38585" Margin="2,0,0,2"/>
<TextBlock Text="{Binding Description}" Margin="5,5,0,15" TextWrapping="Wrap" Foreground="#FF2CE4A4"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</Window.Resources>

<Grid x:Name="LayoutRoot" Background="Bisque" >

<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="{StaticResource PageTitle}"  Foreground="#FFBE2B2B" />
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0">
<ListBox x:Name="lstFriend"
ItemsSource="{Binding Friends}"
ItemTemplate="{StaticResource FriendItemTemplate}" />
</Grid>
</Grid>
</Window>

  • The final effect looks as following

Categories: Article, WPF

WPF – Creating bindings in XAML by StaticResource

Here is a memo of how to create bindings in XAML .

As an example, let’s  look at the following snippet.


<Grid x:Name="LayoutRoot" Background="Transparent" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="{StaticResource PageTitle}"  Foreground="#FFBE2B2B" />
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0">
<ListBox x:Name="lstFriend"
ItemsSource="{Binding Source={StaticResource FriendItemSource}}"
ItemTemplate="{StaticResource FriendItemTemplate}" />
</Grid>
</Grid>

I have defined three types resource in above code.

  • PageTitle : String
  • FriendItemSource : List of custom class
  • ItemTemplate : DataTemplate of each item.

Then , We will add StaticResource into our XAML

  • Add xmlns reference to the namespace.
<Window x:Class="MVVMSample1.Sample1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:MVVMSample1.Model"
Title="WPF Samples" Height="380" Width="280">
  • Add the static resources.

<Window.Resources>
<System:String x:Key="PageTitle">WPF Simplified - Part1</System:String>

<ObjectDataProvider x:Key="FriendItemSource">
<ObjectDataProvider.ObjectInstance>
<x:Array Type="local:Friend">
<local:Friend Name="Shen" Description="This is a sample1" Image="http://goo.gl/I3lmQ" />
<local:Friend Name="Ricardo" Description="This is a sample2" Image="http://goo.gl/fAmHb" />
</x:Array>
</ObjectDataProvider.ObjectInstance>
</ObjectDataProvider>

<DataTemplate x:Key="FriendItemTemplate">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Height="64" Width="64" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2"/>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" HorizontalAlignment="Left" Foreground="#FFF38585" Margin="2,0,0,2"/>
<TextBlock Text="{Binding Description}" Margin="5,5,0,15" TextWrapping="Wrap" Foreground="#FF2CE4A4"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</Window.Resources>

NOTICE

I had to wrap “x:Array ” in an ObjectDataProvider to get it to work, and replace

{StaticResource FriendItemSource}” to “{Binding Source={StaticResource FriendItemSource}}

or not , you will get an error like bellow

“Cannot convert the value in attribute ‘ItemsSource’ to object of type ‘System.Collections.IEnumerable’. ‘System.Windows.Markup.ArrayExtension’ is not a valid value for property ‘ItemsSource’ …”

The final effect looks as following

Full of  XAML source.

<Window x:Class="MVVMSample1.Sample1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:MVVMSample1.Model"
Title="WPF Samples" Height="380" Width="280">

<Window.Resources>
<System:String x:Key="PageTitle">WPF Simplified - Part1</System:String>

<ObjectDataProvider x:Key="FriendItemSource">
<ObjectDataProvider.ObjectInstance>
<x:Array Type="local:Friend">
<local:Friend Name="Shen" Description="This is a sample1" Image="http://goo.gl/I3lmQ" />
<local:Friend Name="Ricardo" Description="This is a sample2" Image="http://goo.gl/fAmHb" />
</x:Array>
</ObjectDataProvider.ObjectInstance>
</ObjectDataProvider>

<DataTemplate x:Key="FriendItemTemplate">
<StackPanel Orientation="Horizontal" Width="223" Margin="2,5,5,2"  Background="Black" >
<Image Source="{Binding Image}" Height="64" Width="64" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2"/>
<StackPanel Orientation="Vertical" Width="auto">
<TextBlock Text="{Binding Name}" HorizontalAlignment="Left" Foreground="#FFF38585" Margin="2,0,0,2"/>
<TextBlock Text="{Binding Description}" Margin="5,5,0,15" TextWrapping="Wrap" Foreground="#FF2CE4A4"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</Window.Resources>

<Grid x:Name="LayoutRoot" Background="Bisque" >

<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="{StaticResource PageTitle}"  Foreground="#FFBE2B2B" />
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0">
<ListBox x:Name="lstFriend"
ItemsSource="{Binding Source={StaticResource FriendItemSource}}"
ItemTemplate="{StaticResource FriendItemTemplate}" />
</Grid>
</Grid>
</Window>
Categories: Article, WPF