Upload
others
View
15
Download
0
Embed Size (px)
Citation preview
CPSC 481 – Tutorial 7More WPF
Brennan Jones
(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 ([email protected]) 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();
}