CPSC 481 Tutorial 7pages.cpsc.ucalgary.ca/~bdgjones/cpsc481/slides/w07.pdfCPSC 481 –Tutorial 7...

Preview:

Citation preview

CPSC 481 – Tutorial 7More WPF

Brennan Jones

bdgjones@ucalgary.ca

(based on tutorials by Alice Thudt, Fateme Rajabiyazdi, and David Ledo)

Plan for Today

• More WPF material, examples, and coding exercises• Mostly stuff that will be applicable to implementing your

vertical prototypes

Reminder

Horizontal prototype due Monday Nov. 2 in lecture:

1. Write-up – including redesign rational (i.e., changes from the first prototype), screen snapshots, and a grading sheet (from assignment handout)

2. Horizontal prototype presentation freeze

• Either email your slides to me (bdgjones@ucalgary.ca) OR submit them on a USB along with your write-up.

Horizontal Prototype Presentations

• Tuesday, November 3 in tutorial (T03)

• Monday, November 9 in tutorial (T01)

More WPF!

User Controls

• Controls are interactive elements in WPF

• Elements such as Buttons, TextBoxes, etc. are Microsoft Controls

• If you want to reuse specific elements in your interface, you can create User Controls

• User Controls can be composites of Microsoft Elements

User Controls

Create your own User Control

Exercise

Create your own User Control, and try to add an instance of it to your MainWindow.xaml.

Transitions

Create three buttons

XAML

Transitions

Create a user control

Transitions

Name it

Transitions

Fill it with content

Transitions

Create two additional user controls and do the same.

Transitions

Add a StackPanel to the Main Window

Give it a name

Transitions

Add the following code to your MainWindow.xaml.csbefore the public MainWindow(){ … } constructor:

// Initialize user controls.UserControl1 page1 = new UserControl1();UserControl2 page2 = new UserControl2();UserControl3 page3 = new UserControl3();

Transitions

Result:

Exercise

Make it so that clicking the buttons at the top changes the current user control.

Solution

Add the following click-event handlers:

private void page1Button_Click(object sender, RoutedEventArgs e){

stackPanel1.Children.Clear();stackPanel1.Children.Add(page1);

}

private void page2Button_Click(object sender, RoutedEventArgs e){

stackPanel1.Children.Clear();stackPanel1.Children.Add(page2);

}

private void page3Button_Click(object sender, RoutedEventArgs e){

stackPanel1.Children.Clear();stackPanel1.Children.Add(page3);

}

User Controls – Exercise

Create a User Control for an Email that looks like this:

Solution – User Control XAML

<Grid Background="White"><Ellipse Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="100" Margin="10,10,0,0" Stroke="Black" VerticalAlignment="Top" Width="100"/><Label Name="SenderText" Content="Sender: Brennan Jones" HorizontalAlignment="Left" Margin="125,19,0,0" VerticalAlignment="Top" FontWeight="Bold"/><Label Name="BodyText" Content="Body of the email..." HorizontalAlignment="Left" Margin="125,50,0,0" VerticalAlignment="Top"/><Canvas Name="DeleteGroup" HorizontalAlignment="Left" Height="25" Margin="401,10,0,0" VerticalAlignment="Top" Width="25" MouseDown="DeleteGroup_MouseDown">

<Ellipse Fill="Red" Height="25" Canvas.Left="0" Stroke="Black" Canvas.Top="0" Width="25"/><Label Content="X" Canvas.Left="0" Canvas.Top="0" Foreground="White"/>

</Canvas></Grid>

Exercise

• Make the Sender a property that can be different in each instance of an email.

• When the delete Button is clicked, the email should be removed from the view.

Solution – User Control C#

public partial class Email : UserControl{

private string sender;public string Sender{

get { return sender; }set {

sender = value;this.SenderText.Content = this.sender;

}}

private string body;public string Body{

get { return body; }set {

body = value;this.BodyText.Content = this.body;

}}

public Email(){

InitializeComponent();}

private void DeleteGroup_MouseDown(object sender, MouseButtonEventArgs e) {(this.Parent as Panel).Children.Remove(this);

}}

Scroll Viewers

<ScrollViewer Height=“auto"><StackPanel Height=“auto" Width=“auto"></StackPanel>

</ScrollViewer>

Exercise

• Create a scroll viewer in your main window that shows 20 emails (using the Email User Control that you just created).• Create a for loop that initializes 20 Email User Controls.

• To make it easy, make the Sender of each email “Sender “ + i• I.e., “Sender 1,” “Sender 2,” “Sender 3,” etc.

Solution – Main Window C#

public MainWindow()

{

InitializeComponent();

for(int i = 0; i < 20; i++)

{

Email email = new Email();

email.Sender = "Sender " + i.ToString();

this.Emails.Children.Add(email);

}

}

Remaining Topics

Which of these do you want me to go over?

• Transitions (Method 2)

• Styles and Templates

• List Boxes

• Simple Animations

• Click & Drag

• Triggers

• Image as a Button

• Timer

Transitions (Method 2)

1. Create three User Controls. Name them: MainMenu, Page1, and Page2.

2. Add a Switcher.cs class to the project. This class will handle switching between different User Controls.

Transitions (Method 2)

using System.Windows.Controls;

public static MainWindow pageSwitcher;

public static void Switch(UserControl newPage){

pageSwitcher.Navigate(newPage);}

Transitions (Method 2)

3. Add the following code to MainWindow.xaml.cs

public MainWindow(){

InitializeComponent();Switcher.pageSwitcher = this;Switcher.Switch(new MainMenu());

}

public void Navigate(UserControl nextPage){

this.Content = nextPage;}

Transitions (Method 2)

4. Design MainMenu, Page1, and Page2 to look like this:

Transitions (Method 2)

5. For MainMenu, Page1, and Page2, add the following events:

private void Button_Click(object sender, RoutedEventArgs e){

Switcher.Switch(new MainMenu());}

private void Button_Click_1(object sender, RoutedEventArgs e){

Switcher.Switch(new Page1());}

private void Button_Click_2(object sender, RoutedEventArgs e){

Switcher.Switch(new Page2());}

Transitions (Method 2)

6. If you ever want to save the state of a page while transitioning (e.g., to save the contents of textboxes, the states of checkboxes etc.), keep the same User Control object. For example:

Page1 page1 = new Page1();

...

private void Button_Click_1(object sender, RoutedEventArgs e){

Switcher.Switch(page1);}

Always use this ‘Page1’ object

Styles and Templates

Source (and excellent resource): http://www.codeproject.com/Articles/84630/WPF-Customize-your-Application-with-Styles-and-Con

Styles and Templates

Start by creating a button

Styles and Templates

Add this ‘Resources’ block

Styles and Templates

Insert this code:This defines the style of buttons in your XAML.

The button’s appearance changes

Styles and Templates

You can also give it a key…

…and set specific buttons to the particular style.

Styles and Templates

You can create define Control Templates

• These define how a control is drawn

Styles and Templates

Example:<UserControl.Resources>

<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}"><Setter Property="Template">

<Setter.Value><ControlTemplate TargetType="{x:Type Button}">

<Border Name="fondoboton" BorderBrush="DarkGray" BorderThickness="5" CornerRadius="10,0,10,0" Background="LightGray">

<ContentPresenter Name="contenido" Content="{Binding Path=Content, RelativeSource={RelativeSourceTemplatedParent}}"></ContentPresenter>

</Border></ControlTemplate>

</Setter.Value></Setter>

</Style></UserControl.Resources>

Styles and Templates

Result:

Styles and Templates

You can also define a default background colour:<UserControl.Resources>

<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}"><Setter Property="Background" Value="Orange" /><Setter Property="Template">

<Setter.Value><ControlTemplate TargetType="{x:Type Button}">

<Border Name="fondoboton" BorderBrush="DarkGray" BorderThickness="5" CornerRadius="10,0,10,0" Background="{TemplateBinding Background}">

<ContentPresenter Name="contenido" Content="{Binding Path=Content, RelativeSource={RelativeSourceTemplatedParent}}"></ContentPresenter>

</Border></ControlTemplate>

</Setter.Value></Setter>

</Style></UserControl.Resources>

Add this line

Add this

Styles and Templates

Result:

Styles and Templates

For a specific button, you can set the background colour to something other than the ‘default’, while keeping everything else the same as the template.

• Without changing the template, change the button’s XAML code to something like this:

<Button Style="{StaticResource MyButtonStyle}" Background="Red"Content="Button" HorizontalAlignment="Left" Margin="119,234,0,0" VerticalAlignment="Top" Width="75"/>

Background is ‘Red’

Styles and Templates

Result:

Exercise

Define a template for a button (like the one shown in the previous slides) that has a default border colour of black, and create a button of that template that has the border colour blue.

Solution

<UserControl.Resources><Style x:Key="MyButtonStyle" TargetType="{x:Type Button}">

<Setter Property="BorderBrush" Value="Black" /><Setter Property="Template">

<Setter.Value><ControlTemplate TargetType="{x:Type Button}">

<Border Name="fondoboton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="5" CornerRadius="10,0,10,0">

<ContentPresenter Name="contenido" Content="{Binding Path=Content, RelativeSource={RelativeSourceTemplatedParent}}"></ContentPresenter>

</Border></ControlTemplate>

</Setter.Value></Setter>

</Style></UserControl.Resources>

Solution

Result:

Solution (cont’d)

<Button Style="{StaticResource MyButtonStyle}" BorderBrush="Blue" Content="Button" HorizontalAlignment="Left" Margin="119,234,0,0" VerticalAlignment="Top" Width="75"/>

Solution

Result:

List Boxes

Drag a ListBox into the XAML

List Boxes

Click to modify items

List Boxes

Click to addSelect ‘ListBoxItem’

List Boxes

Change content

List Boxes

Result:

List Boxes

Can get the currently-selected index:listbox1.SelectedIndex

Exercise

• Write code that changes the text in a textbox whenever a listbox’s selection is changed.

Solution – C#

Add ‘SelectionChanged’ event handler.

private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e){

textbox1.Text = (String)((ListBoxItem)listBox1.SelectedItem).Content;

}

Simple Animations

Insert an ellipse into your XAML

Simple Animations

XAML code:<Ellipse Name="ellipse1" Fill="#FFE82828" HorizontalAlignment="Left" Height="118" Margin="10,151,0,0" Stroke="Black" VerticalAlignment="Top" Width="127"/>

Simple Animations

Add this code to the top of your C# file:using System.Windows.Media.Animation;

Add this inside your user control class before the constructor:

private Storyboard myStoryboard;

Simple Animations

Add this code to your user control constructor:// animate fade in and fade outDoubleAnimation animation = new DoubleAnimation();animation.From = 1.0;animation.To = 0.0;animation.Duration = new Duration(TimeSpan.FromSeconds(5));animation.AutoReverse = true;animation.RepeatBehavior = RepeatBehavior.Forever;

myStoryboard = new Storyboard();myStoryboard.Children.Add(animation);Storyboard.SetTargetName(animation, ellipse1.Name);Storyboard.SetTargetProperty(animation, new PropertyPath(Ellipse.OpacityProperty));

// Use the Loaded event to start the Storyboard.ellipse1.Loaded += new RoutedEventHandler(myEllipseLoaded);

Simple Animations

Add this function:private void myEllipseLoaded(object sender, RoutedEventArgs e)

{

myStoryboard.Begin(this);

}

Exercise

• Change the animation so that the width of the ellipse is animated.

Solution

// animate fade in and fade outDoubleAnimation animation = new DoubleAnimation();animation.From = 120.0;animation.To = 240.0;animation.Duration = new Duration(TimeSpan.FromSeconds(5));animation.AutoReverse = true;animation.RepeatBehavior = RepeatBehavior.Forever;

myStoryboard = new Storyboard();myStoryboard.Children.Add(animation);Storyboard.SetTargetName(animation, ellipse1.Name);Storyboard.SetTargetProperty(animation, new PropertyPath(Ellipse.WidthProperty));

// Use the Loaded event to start the Storyboard.ellipse1.Loaded += new RoutedEventHandler(myEllipseLoaded);

Click & Drag

Create an ellipse

Give it a name

Click & Drag

Give the grid a name too

Click & Drag

Add MouseDown

Add MouseMove

Add MouseUp

Click & Drag

In your C# file:bool captured = false;

...

private void Ellipse_MouseDown_1(object sender, MouseButtonEventArgs e){

captured = true;}

private void Ellipse_MouseUp_1(object sender, MouseButtonEventArgs e){

captured = false;}

private void Ellipse_MouseMove_1(object sender, MouseEventArgs e){

if (captured){

Thickness margin = ellipse1.Margin;margin.Left = e.GetPosition(_mainGrid).X - (ellipse1.Width / 2);margin.Top = e.GetPosition(_mainGrid).Y - (ellipse1.Height / 2);ellipse1.Margin = margin;

}}

Triggers

Source (and excellent resource): http://www.wpf-tutorial.com/styles/trigger-datatrigger-event-trigger/

Triggers

• Allow you to change the value of a given property once a certain condition changes• Allow you to do this entirely in XAML

• Three types:1. Property trigger

2. Data trigger

3. Event trigger

Triggers

Property trigger

• Defined by <Trigger> element.

• Watches a specific property on the owner control, and whenever that property has a specific value, it changes other properties.

Triggers

Property trigger example<TextBlock Text="Hello, styled world!" FontSize="28" HorizontalAlignment="Center" VerticalAlignment="Center">

<TextBlock.Style>

<Style TargetType="TextBlock">

<Setter Property="Foreground" Value="Blue"></Setter>

<Style.Triggers>

<Trigger Property="IsMouseOver" Value="True">

<Setter Property="Foreground" Value="Red" />

<Setter Property="TextDecorations" Value="Underline" />

</Trigger>

</Style.Triggers>

</Style>

</TextBlock.Style>

</TextBlock>

Triggers

Property trigger example

• Result:• Underlines and colours the text red on mouse-over.

Triggers

Data trigger

• Defined by <DataTrigger> element.

• Watches a specific property that can be anywhere (not specifically on the owner control), and whenever that property has a specific value, it changes other properties.

Triggers

Data trigger example<CheckBox Name="cbSample" Content="Hello, world?" />

<TextBlock HorizontalAlignment="Center" Margin="0,20,0,0" FontSize="48">

<TextBlock.Style>

<Style TargetType="TextBlock">

<Setter Property="Text" Value="No" />

<Setter Property="Foreground" Value="Red" />

<Style.Triggers>

<DataTrigger Binding="{Binding ElementName=cbSample, Path=IsChecked}" Value="True">

<Setter Property="Text" Value="Yes!" />

<Setter Property="Foreground" Value="Green" />

</DataTrigger>

</Style.Triggers>

</Style>

</TextBlock.Style>

</TextBlock>

Triggers

Data trigger example

• Result:• Changes the text to “Yes!” and the text colour to green

when the checkbox is checked.

Triggers

Event trigger

• Defined by <EventTrigger> element.

• Triggers in response to an event being called.• Triggers exactly once that event is called.

Triggers

Event trigger example<TextBlock Name="lblStyled" Text="Hello, styled world!" FontSize="18" HorizontalAlignment="Center" VerticalAlignment="Center">

<TextBlock.Style><Style TargetType="TextBlock">

<Style.Triggers><EventTrigger RoutedEvent="MouseEnter">

<EventTrigger.Actions><BeginStoryboard>

<Storyboard><DoubleAnimation Duration="0:0:0.300" Storyboard.TargetProperty="FontSize" To="28" /></Storyboard>

</BeginStoryboard></EventTrigger.Actions>

</EventTrigger>

...

Triggers

Event trigger example (cont’d)...

<EventTrigger RoutedEvent="MouseLeave"><EventTrigger.Actions>

<BeginStoryboard><Storyboard><DoubleAnimation Duration="0:0:0.800" Storyboard.TargetProperty="FontSize" To="18" /></Storyboard>

</BeginStoryboard></EventTrigger.Actions>

</EventTrigger></Style.Triggers>

</Style></TextBlock.Style>

</TextBlock>

Triggers

Event trigger example

• Result:• Animation enlarges the text on mouse-over, and shrinks

it back to its original size on mouse-leave.

Image as Button

Exercise: Create a button that displays as an image.

Image as Button

Solution:

<Grid><Button Background="Transparent" HorizontalAlignment="Left" Margin="117,84,0,0" VerticalAlignment="Top" Width="361" Height="231">

<Image Name="img1" Stretch="Fill" Source="Testimg.png" Margin="10"/>

</Button></Grid>

Timer

• We are going to make use of a DispatchTimer.

• Make sure you are using the Threading namespace.

• Add a TextBlock on your code. We will change the value every time the timer ticks.

XAML

<Grid Name="Grid"><TextBlock Name=”timerText" FontSize="48” HorizontalAlignment="Center" VerticalAlignment="Center" />

</Grid>

C#

using System.Windows.Threading;

public void DispatcherTimerSample(){

InitializeComponent();DispatcherTimer timer = new DispatcherTimer();timer.Interval = TimeSpan.FromSeconds(1);timer.Tick += timer_Tick;timer.Start();

}

void timer_Tick(object sender, EventArgs e){

timerText.Content = DateTime.Now.ToLongTimeString();

}