<Button Content="Hello"/>
<Label Content="Hello"/>
<CheckBox Content="Hello"/>
<Button>
<Button.Content>
<Rectangle Width="30" Height="5" Fill="Yellow" Stroke="AliceBlue"/>
</Button.Content>
</Button>
多个元素类型,正确的使用方式:<Button>
<StackPanel Orientation="Horizontal">
<Rectangle Width="15" Height="10" Fill="Yellow" Stroke="AliceBlue" Margin="0 0 5 0"/>
<TextBlock Text="暂停"/>
</StackPanel>
</Button>
<GroupBox Header="测试">
<TextBlock Text="This' a test demo"/>
</GroupBox>
<TabControl Height="100">
<TabItem Header="测试页面一"/>
<TabItem Header="测试页面二"/>
<TabItem Header="测试页面三"/>
</TabControl>
<TabControl ItemsSource=""/>
布局原则:先整体规划(Grid),再局部规划(Grid、StackPanel等)
Grid.RowDefinitions:可以创建任意多行
Grid.ColumnDefinitions:可以创建任意多列
ColumnSpan:用于设置空间元素的跨列
RowSpan:用于设置空间元素的阔行
ShowGridLines:可以设置边距线的显示
LastChildFill:容器中的最后一个元素时,默认该元素填充DockPanel所有空间,默认值为True
一个窗口中中能包含一个元素。
控件的布局应该有容器来决定,而不是通过自身使用margin之类的东西来控制位置。
控件应避免明确的定义具体的尺寸,因为显示器分辨率及windows窗体的大小都有可能随时改变,如果明确的定义尺寸。
当窗体变动后就会出现大面积的空白或是缺失。但为了控件功能及效果的展示,应该限定一个可接受的最大及最小尺寸。通过MinWidth, MinHeight, MaxWidth, MaxHeight属性可以实现这一点。
不要将界面元素位置设置成与屏幕坐标相关。
容器应将有效空间共享给其子控件,这也是为了不在窗体调整后,遗留出大块的空余。
容器嵌套使用,因为不同的容器,表现效果不同,必要时应结合使用。
Text={Binding ElementName=slider,Path=Value}
<StackPanel Grid.Row="1" Grid.Column="2">
<TextBox Text="{Binding ElementName=slider, Path=Value, Mode=OneWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Slider x:Name="slider" Width="200" Value="50" Minimum="0" Maximum="100"/>
</StackPanel>
上面的代码中,使用的绑定方式是根据元素的方式: ElementName=xxx, 如需绑定到一个非元素的对象, 则有一下几属性:
属性值 | 含义 |
---|---|
Source | 提供数据的对象本身 |
RelativeSource | 使用RelativeSource对象指向源目标 |
DataContext | 从当前元素向下, 找到第一个非空的DataContext属性 |
<Window.Resources>
<TextBox x:Key="txt1">测试绑定到非元素上</TextBox>
</Window.Resources>
<Grid>
<TextBlock Text="{Binding Source={StaticResource ResourceKey=txt1},Path=Text}"/>
</Grid>
<StackPanel Grid.Row="1" Grid.Column="2" Width="220">
<StackPanel Width="150"/>
<!-- TextBlock 的Text值为220 -->
<TextBlock Text="{Binding Path=Width, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}}"/>
</StackPanel>
public MainWindow()
{
InitializeComponent();
submit0.Content = "我运行了";
//this.DataContext = new Test() { Name = "小明" };
PageModel page = new PageModel();
page.ClassName = "高二三班";
page.Students = new List();
page.Students.Add(new Student() { Name = "张三", Age = "18", Sex = "男" });
page.Students.Add(new Student() { Name = "李四", Age = "19", Sex = "女" });
page.Students.Add(new Student() { Name = "王五", Age = "20", Sex = "男" });
// 将page绑定到DataContext上
this.DataContext = page;
}
public class PageModel
{
public string ClassName { get; set; }
public List Students { get; set; }
}
public class Student
{
public string Name { get; set; }
public string Age { get; set; }
public string Sex { get; set; }
}
前台代码:<Grid Grid.Column="3">
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Margin="5 0 0 0" Text="班级名称:"/>
<TextBlock Margin="5 0 0 0" Text="{Binding ClassName}"/>
</StackPanel>
<DataGrid Grid.Row="1" ItemsSource="{Binding Students}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="名称" Binding="{Binding Name}"/>
<DataGridTextColumn Header="年龄" Binding="{Binding Age}"/>
<DataGridTextColumn Header="性别" Binding="{Binding Sex}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
在上面的示例中, 从 源对象 -> 目标对象, 源对象的值发生改变, 目标会立刻响应。但是从目标 -> 源 , 未必会立即发生。
在WPF中,他们的行为右 binding中的 UpdateSourceTrigger属性控制, 关于UpdateSourceTrigger 下面列出了对应的枚举值。
属性值 | 含义 |
---|---|
propertyChanged | 当目标属性发生变化时立即更新源目标 |
LostFocus | 当目标属性发生变化时并且目标丢失焦点是更新源目标 |
Explicit | 除非调用BindingExpression.UpdateSource()方法, 否则无法更新资源 |
Default | 根据目标属性的元数据确定更新行为, 大多数属性的默认行为是PropertyChanged, 但是TextBox.Text的属性默认行为是LostFocus |
ControlTemplate(控件模板)不仅是用于来定义控件的外观、样式, 还可通过控件模板的触发器(ControlTemplate.Triggers)修改控件的行为、响应动画等。
TemplateBinding 可以理解为, 通过模板绑定关联到指定的样式、属性。 如此一来 , 当按钮通过显示设置该属性, 则最终会影响着Template绑定的属性值。
下面将通过代码演示, 有 TemplateBinding 和 无TemplateBinding 的区别, 在Button按钮中, 显示定义 按钮的边框颜色为 “Blue”, 分别看两者中的影响:
可以理解, TemplateBinding 主要的作用为, 与外部的属性关系起来, 使其达到改变样式属性的作用。
检测到鼠标等的行为,通过TargetName找到Border控件,并进行改变控件相应Property的属性外观
下面定义了一个EventTrigger 事件触发器,
当鼠标进入按钮区域时, 执行一个0.5秒的动画, 将按钮的背景颜色设置为 pink,
当鼠标离开按钮区域时, 执行一个0.5秒的动画,将按钮的背景颜色设置为Green。
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetName="border"
Storyboard.TargetProperty="Background.Color" To="Pink"
Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
控件模板可以独立存在, 上面的例子中, 包含在样式文件中, 下面, 单独声明一个独立的控件模板:
<Window.Resources>
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
<Border Background="Red" CornerRadius="20">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"/>
</Border>
</ControlTemplate>
<Style x:Key="demo1" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="FontSize" Value="30"/>
</Style>
</Window.Resources>
<Button Style="{StaticResource demo1}" Width="100" Height="50" Margin="0 5 0 0" Template="{StaticResource ButtonTemplate}" Content="World" VerticalAlignment="Center" HorizontalAlignment="Center"/>
下面用一个例子, 来演示CellTemplate使用。例子实现一个DataGrid 展示一个普通的数据标, 同时新增一列CellTemplate添加两个自定义的按钮, 如下所示:
<DataGrid Grid.Row="1" ItemsSource="{Binding Students}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="名称" Binding="{Binding Name}"/>
<DataGridTextColumn Header="年龄" Binding="{Binding Age}"/>
<DataGridTextColumn Header="性别" Binding="{Binding Sex}"/>
<DataGridTemplateColumn Header="操作">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Button Content="编辑"/>
<Button Content="删除" Margin="5 0 0 0"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
最终的效果, 在数据的表格最后一列, 将会在一列中分别生成 两个普通按钮。
在列表的控件中, 常常会出现一些需求, 类似在下拉控件或树控件中添加一个 CheckBox选择框, 一个图标或图片, 这个时候, 我们就可以利用自定义的DataTemplate 来实现这个功能。
接下来, 用一个示例来简单演示其功能, 同样, 该例子演示利用 ListBox 和 ComboBox来绑定一个 颜色代码列表, 同时展示其颜色。
<Window.Resources>
<DataTemplate x:Key="comTemplate">
<StackPanel Orientation="Horizontal" Margin="5,0">
<Border Width="10" Height="10" Background="{Binding Code}"/>
<TextBlock Text="{Binding Code}" Margin="5,0"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<ComboBox Name="cob" Width="120" Height="30" ItemTemplate="{StaticResource comTemplate}"/>
<ListBox Name="lib" Width="120" Height="100" Margin="5,0" ItemTemplate="{StaticResource comTemplate}"/>
</StackPanel>
</Grid>
后台代码:
public MainWindow()
{
InitializeComponent();
List ColorList = new List();
ColorList.Add(new Color() { Code = "#FF8C00" });
ColorList.Add(new Color() { Code = "#FF7F50" });
ColorList.Add(new Color() { Code = "#FF6EB4" });
ColorList.Add(new Color() { Code = "#FF4500" });
ColorList.Add(new Color() { Code = "#FF3030" });
ColorList.Add(new Color() { Code = "#CD5B45" });
cob.ItemsSource = ColorList;
lib.ItemsSource = ColorList;
}
public class Color
{
public string Code { get; set; }
}
定义ItemsControl 主要分两个步骤: 1.设置ItemsPanel容器, 用于容纳列表的最外层容器 2.定义子项的DataTemplate
<ItemsControl Name="ic">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/> //ItemsPanel的容器需要满足一个条件, 则是属于Panel族的元素
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="50" Height="50" Content="{Binding Code}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
上面代码中, 定义了一个WarpPanel 容器为ItemsControl的 最外层容器, 子项数据模板则绑定了一个按钮, 后台代码绑定几条数据, 查看其效果: 横排排列五个按钮, 内容分别是 1~6.
List tests = new List();
tests.Add(new Test() { Code = "1" });
tests.Add(new Test() { Code = "2" });
tests.Add(new Test() { Code = "3" });
tests.Add(new Test() { Code = "4" });
tests.Add(new Test() { Code = "6" });
ic.ItemsSource = tests;