• WPF 深入研究 之 Control 控件


     

    这一章介绍Control 控件。

    本章共计51个示例,全都在VS2008下.NET3.5测试通过,点击这里下载:  Controls.rar

    关于在VS2008新建WPF类库时,有Custom ControlUser Control两个选择。

    User Control类库会在工程中建立一个XAML文件及其绑定后台代码,前者以<UserControl开头,后者是一个派生自UserControl的类。

    Custom Control类库则在工程中创建派生自Control基类的CustomControl1控件,并在构造函数中为CustomControl1指定依赖属性DefaultStyleKeyProperty

        public class CustomControl1 : Control
        
    {
            
    static CustomControl1()
            
    {
          DefaultStyleKeyProperty.OverrideMetadata(
    typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
            }

        }

    此外,Custom Control类库还会在Theme目录下生成Generic.xaml,这是一个以<ResourceDictionary开头的文件,里面存放着CustomControl1的控件模板。

    1AccessText

    这个例子介绍了AccessText元素的使用,以及如何指定一个访问键。

    AccessText标记可以使用在任何控件中,如ButtonLabel等,通过在字母前加一个下划线指定该字母为访问键。连着两个下划线,可以显示一个下划线。

    还有就是本例是以StackPanel开始的,并不是一个传统的PageWindow——所有的布局控件都可以这么使用。相应的后台代码文件要继承StackPanelVS2008目前还不支持创建这样的文件,只有手动设置。例如一个VS2008创建的一个名为Pane1Page,先修改页面标记PageStackPanel,这时,后台的Page1.g.i.cs文件会自动改为Pane1StackPanel,然后再修改Pane1.xaml.cs文件,使Pane1继承于StackPanel。这样就建立了一个所谓的StackPanel控件。

    小技巧,在VS2008中,以StackPanel开始的页面的Design视图是没有滚动条的,这就只能看到左上部分——如果xaml内容很多的话,我们可以使用Design视图右上角的“比例尺”,缩小比例可以看到全部试图。

    2BtnColor

    按钮中可以放入任何控件,如image

    注意到OnClick4方法。由于设置了背景色,所以按钮点击后的效果看上去像是一个颜色渐变的动画。这是由WPF本身提供的,即使是不设置背景色,点击后也会有颜色渐变效果,只是不太明显而已。

    最后一个按钮的点击事件,创建了一个新的按钮加入到XAML控件树中,这与传统的WinForm模型是一致的。

    仍然是最后一个按钮,按钮并没有Name值——这是允许的,也会在编译时被序列化,只是这个按钮的Name值为空,而其它属性都还是存在的。

    3BtnStyles

    这个示例使用Application级别的资源为Button设定样式。

    注意Button标签中Style="{StaticResource SystemResStyle}的使用,以及<Application.Resources>中样式资源,有3种:

    一种是直接的键值对,是静态的:

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

    一种是使用WPF内嵌的资源,是动态的:

                <Setter Property = "FontWeight" Value= "{DynamicResource {x:Static SystemFonts.MessageFontWeightKey}}"/>

    还有一种是触发器,当条件满足时,属性会相应改变:

                <Style.Triggers>

                    <Trigger Property="Button.IsMouseOver" Value="true">

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

                    </Trigger>

                    <Trigger Property="Button.IsPressed" Value="true">

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

                    </Trigger>

                </Style.Triggers>

    4BulletPanel

    Bullet用于绘制一组对象的列表。原有的BulletPanel标签已经被BulletDecorator取代。

    Bullet中可以放置任何UI元素,如CheckBoxTextBoxRadioButtonImageTextBlock以及任何Shape。下面是一个标准的用法:

            <BulletDecorator >

                <BulletDecorator.Bullet>

                    <Image Source="images"apple.jpg"/>

                </BulletDecorator.Bullet>

                <TextBlock Name="FontSizeExample">XXX</TextBlock>

            </BulletDecorator>

    这里把Image作为Bullet,而把TextBlock作为BulletDecorator的子一级元素。ImageTextBlock是可以互换的,没有区别,但是标签<BulletDecorator.Bullet>中只能有一个UI元素。

    WindowLoaded负责创建一个BulletDecorator控件,也就是图中左下角的那个。其中创建一个BitmapImage的代码如下:

                BitmapImage myBitmapImage = new BitmapImage();

                myBitmapImage.BeginInit();

                myBitmapImage.UriSource = new Uri(@"pack://application:,,/images/apple.jpg");

                myBitmapImage.EndInit();

    ——这是值得我们学习的。

    *补注:面板本身没有文本内容,它们是其他元素的矩形容器。总共有七种面板,每种的布局都不同:BulletPanelCanvasDockPanelGridStackPanelTabPanelToolBarOverflowPanel。面板可以层层嵌套。

    5CheckBoxElement

    WPF对多选框控件也提供了同样的支持,只是更加简单,每个条目都是一个CheckBox,有自己的Name,以及可以内嵌其它控件。

    CheckBoxCheckedUnchecked事件,分别当该条目被选中和被取消选中时触发。

    6CheckBoxStyles

    这个示例使用Application级别的资源为CheckBox设定样式。

    具体参见示例4 BtnStyles

    7ComboBoxItems

    这个示例分为两部分。

    首先是ComboBoxItems的显示,以下是部分截取的代码:

            <ComboBox Name="cb".

                 IsEditable="true" Text="Open Combo Box" IsReadOnly="true"

                 StaysOpenOnEdit="true" IsDropDownOpen="true">

                <ComboBoxItem>Spain - Item 0</ComboBoxItem>

            </ComboBox>

    它的Text属性是默认的显示值,只在初始的时候作为默认选中项显示,当选中列表中一项时,这和个Text属性值就不再存在了。

    IsEditableIsReadOnly属性共同决定了选项是否可以更改,但改动过的值不会影响该项原先的值——已经存储在后台,必须写代码进行手动修改。

    StaysOpenOnEdit属性决定了在编辑时——也就是光标定位在ComboBox的文本框时,下拉框是否仍然显示。

    IsDropDownOpen属性决定了默认下拉框是否为打开的,比如说窗体第一次显示时。

    其次,就是那个Menu控件的作用了——通过MenuItemClick事件,根据MenuItemHeader值,也就是ComboBox中相应的索引值,来决定显示哪一个ComboBoxItem

                ComboBoxItem cbi = (ComboBoxItem)

                     (cb.ItemContainerGenerator.ContainerFromIndex(index));

    这条语句重新得到了该索引的ComboBoxItem

    8ComboBoxSimple

    这个例子和示例-8是一样的,从略。

    9ComboBoxStyles

    这个例子演示了4个不同样式的ComboBox。这些样式来自Apllication级别。

    第一个是SimplePlus样式,设定了默认打开下拉列表(IsDropDownOpen=true)和下拉列表的高度(MaxDropDownHeight=30)。

    第二个是Simple样式,设定了前景色和背景色。

    第三个仍然使用Simple样式,但给其内部的ComboBoxItem使用SimpleComboBoxItem样式,这就覆盖了之前设置的样式。

    最后一个使用了触发器。这使得当鼠标移动到ComboBoxItem时,相应的选项会发生变化。

    10ContentControl

    ContentControl表示包含单项内容的控件,这是一个基类,很多控件都派生于此,如FrameListBoxItemWindow等等。我们也可以直接使用它。

    ContentControl具有有限的默认样式,为此我们要额外设计样式以及控件模板:

                <Style x:Key="ContentCtrl" TargetType="{x:Type ContentControl}">

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

                    <Setter Property="FontSize" Value="20"/>

                    <Setter Property="FontWeight" Value="Bold"/>

                    <Setter Property="Template">

                        <Setter.Value>

                            <ControlTemplate TargetType="{x:Type ContentControl}">

                                <ContentControl ContentControl.Content="Hello"/>

                            </ControlTemplate>

                        </Setter.Value>

                    </Setter>

                </Style>

    这就添加了前景色、字体大小和宽度、以及一个指定了显示内容的控件模板,那么第一个ContentControl就使用这套样式:

                <ContentControl Style="{StaticResource ContentCtrl}"/>

    或者创建一个新的数据模板,

                <DataTemplate x:Key="template1">

                    <TextBlock Text="{Binding}" FontSize="12" FontWeight="Bold" TextWrapping="Wrap"></TextBlock>

                </DataTemplate>

    第二个ContentControl使用了这个模板:

                <ContentControl Name="contCtrl" ContentTemplate="{StaticResource template1}"

    注:DataTemplate不仅仅用于数据绑定

    此外,ContentControl有一个HasContent属性,用以判断控件的Content是否有值。

    11ContextMenu

    这个例子演示了ContextMenu的种种用法。

    我们可以把ContextMenu捆绑到Button上:

            <Button Name="cmButton" Height="30">Button with Context Menu

                <Button.ContextMenu>

                    <ContextMenu Name="cm" Opened="OnOpened" Closed="OnClosed" StaysOpen="true">

                        <MenuItem Header="File"/>

                        <MenuItem Header="Recent Files">

                            <MenuItem Header="ReadMe.txt"/>

                        </MenuItem>

             ……

    ContextMenu上可以设置OnOpened事件和OnClosed事件,分别发生在上下文菜单弹出和关闭时。StaysOpen属性为false,表示上下文菜只能弹出一次,当鼠标再次右击,不会再弹出;否则,不加限制。

    还可以为MenuItem添加tooltip

                            <MenuItem Header="Context Menu item with ToolTip">

                                <MenuItem.ToolTip>

                                    <ToolTip>

                                        Some information.

                                    </ToolTip>

                                </MenuItem.ToolTip>

                            </MenuItem>

    或者在MenuItem中加入图片:

                            <MenuItem>

                                <MenuItem.Header>

                                    <Image Source="data/cat.png"/>

                                </MenuItem.Header>

                            </MenuItem>

    也可以为一个disabled的控件添加ContextMenu,比如说Button,只要设定它的属性:

    ContextMenuService.ShowOnDisabled="True"

    最后,就是在代码中动态添加一个ContextMenu,见OnClick方法。

    12ContextMenuShared

    This example describes how to create a ContextMenu that can be associated with more than one control. For example the ContextMenu is associated with both a Button and a CheckBox.

    这个例子演示了两个Button和两个CheckBox共享页面资源中同样的ContextMenu

    页面资源如下:

    <ContextMenu x:Key="MyContextMenu" x:Shared="true">

        <MenuItem Header="This MenuItem is checkable" IsCheckable="true" />

        <Separator/>

        <MenuItem Header="This is a regular MenuItem" />

    </ContextMenu>

    这里的关键是x:Shared,当设置为 false时,会修改 WPF资源检索行为,以便资源请求会为每个请求创建一个新实例,而不是所有请求共享同一个实例。

    指定x:Shared="true"并不常见,因为这已经是默认行为,意味着任何给定资源请求都始终返回同一个实例。对于x:Shared,没有直接的代码等效项。

    x:Shared只有在满足以下条件的情况下才是合法的:

    ·包含具有x:Shared的项的ResourceDictionary必须已编译。ResourceDictionary不能在松散XAML内或用于主题。(所以本例要指定x:Class

    ·包含项的ResourceDictionary不得嵌套在另一个 ResourceDictionary内。例如,不能对已经是ResourceDictionary项的Style内部的ResourceDictionary中的项使用 x:Shared

    四个控件使用方法都是一样的,以其中一个为例:

    <Button Background="LightBlue"

        Content="This Button has a ContextMenu"

        ContextMenu="{DynamicResource MyContextMenu}" />

    注意:MenuItemIsCheckable属性,表示菜单项是否可以被挑勾以表示选中。

    13ContextMenuStyles

    本例与示例-8的大同小异,不再敷述。

    14ControlProps

    本例依次展示了如何在代码中读写按钮的10个属性。分别是:BackgroundForegroundFontFamilyFontSizeFontStyleFontWeightBorderBrushHorizontalContentAlignmentVerticalContentAlignment,最后,还有Content

    15ControlsAll

    本例通过按钮的ContextMenu菜单,依次从代码中创建了14个基本控件,分别是

    ButtonCheckBoxComboBoxContextMenuListBoxMenuRadioButtonRepeatButtonScrollBarSliderTextBoxThumbToolBarToolTip

    以上各种控件的用法散见于本章各示例中。

    16ControlTemplateExamples

    这个例子演示了如何为控件创建新的样式,这里使用了大量的控件模板。

    1

    最后,介绍一下ResourceDictionary技术。

    74UserControlNumericUpDown

    这个示例演示了如何创建一个WPF用户控件。

    为此要建立一个派生于UserControlXAML,在其中添加若干元素。这里设计了依赖属性ValueProperty,将其绑定到要调整的数值,并为其添加了OnValueChanged事件,触发于这个值改动的时候,会导致TextBlock中的值相应有所变动。

    我们使用到了WPF事件机制的后台编程方式,这是对CLR 基本事件模型的更高级封装。其中涉及到了依赖属性。

    而在页面中,可以很简单的使用这个用户控件:

    <Window xmlns:uc="clr-namespace:UserControlNumericUpDown">

        <Grid>

            <uc:NumericUpDown />

        </Grid>

    </Window>

    17CustomControlNumericUpDown

    18CustomControlNumericUpDownExternalLibrary

    19CustomControlNumericUpDownOneProject

    调试不出来

    20Expander

    Expander表示一个可显示标题的控件,该标题具有一个用于显示内容的可折叠窗口。对折叠的控制表现为IsExpanded属性,可以把Expander中的所有内嵌元素都隐藏起来。

    此外,它的ExpandDirection属性的枚举值,Down、Up、LeftRight

                    myExpander.ExpandDirection = ExpandDirection.Down;

    ExpandDirection属性决定了内容窗口的打开方向,以下是四个枚举分别对应的效果:

    注:我们还可以在折叠与展开的时候,使用事件CollapsedExpanded,自定义一些小逻辑。

    21ExpanderRichContent

    Expander的内容属性为Content,标题属性为Header,都可以嵌入控件元素,如这个示例,Header中放置了一个BulletDecorator

                    <Expander.Header>

                        <BulletDecorator>

                            <BulletDecorator.Bullet>

                                <Image Width="10" Source="images"icon.jpg"/>

                            </BulletDecorator.Bullet>

                            <TextBlock Margin="20,0,0,0">My Expander</TextBlock>

                        </BulletDecorator>

                   </Expander.Header>

    Content中放置了一个ScrollViewer

                    <Expander.Content>

                        <ScrollViewer Height="50">

    注:如果展开窗口的内容对于窗口而言太大,您可以在 ScrollViewer 控件中换行显示 Expander 的内容,以便提供可滚动的内容。Expander 控件未自动提供滚动功能。

    若要使 Expander 能够正确工作,当 ExpandDirection 属性设置为 Down 或 Up 时,请不要在 Expander 控件上指定 Height。同样,当 ExpandDirection 属性设置为 Left 或 Right 时,不要在 Expander 控件上指定 Width。如果在 Expander 控件显示展开内容的方向上设置大小,将显示由大小参数定义的区域,并在其周围显示一个边框。即使在窗口折叠时,仍会显示该区域。若要设置展开窗口的大小,请在 Expander 控件的内容上或在封闭内容的 ScrollViewer 上设置大小维度。

    如果 Expander 控件是 DockPanel 中的最后一个元素,Expander 的大小将设置为填满 DockPanel 的其余区域。若要防止此行为,请将 DockPanel 的 LastChildFill 属性设置为 false,或者确保 Expander 不是 DockPanel 中的最后一个元素。

    22FrameExample

    Frame这个控件,多是用来承载导航的XAMLHTML页面的。

            <Frame Name = "myFrame" Grid.Column="0" Grid.Row="1" Background="LightBlue"/>

    以下是导航语句:

                    myFrame.Navigate(new System.Uri("http://msdn.microsoft.com/vcsharp/"));

                    myFrame.Navigate(new System.Uri("AnotherAvalonPage.xaml",

                                     UriKind.RelativeOrAbsolute));

    23GridSplitterProperties

    这个示例演示了GridSplitter控件的使用。

    GridSplitter派生于Thumb,只能使用于Grid中,而且要指定它所在的rowcolumn位置(以及行和列的span)

                GridSplitter split = new GridSplitter();

                split.Width = 6;

                grid.Children.Add(split);

                Grid.SetColumn(split, 2);

                Grid.SetRow(split, 1);

    GridSplitter可以和其它元素共享相同的单元格,这就存在谁遮挡了谁的问题——后出现的元素总是在前面,为此,需要设置margin,以避免重叠:

                split.Margin = new Thickness(10);

    一般会让GridSplitter跨越整行或整列,比较合理:

                Grid.SetRowSpan(split, 3);

                Grid.SetColumnSpan(split, 3);

    一般把GridSplitter单独放在一个或一组单元格中

    在这个例子中,通过设置GridSplitter的左对齐,可以看到遮挡效果,因为在Grid11)这个位置,有两个元素,GridSplitter是后出现的元素:

            <StackPanel Grid.Row="1" Grid.Column="1" Background="Red">

                <TextBlock>Row 1 Col 1</TextBlock>

            </StackPanel>

            <GridSplitter Name="myGridSplitter" Grid.Column="1" Grid.Row="1" Width="5"/>

    下面讨论GridSplitter的几个属性:

    1ShowsPreview属性,默认为fasle,也就是说拖动GridSplitter时看不到效果。而设置为true,则要等到松开鼠标,鸽子才会改变尺寸。

    2DragIncrement属性,设置这个值,可以决定每次鼠标拖动的距离。默认值为1

    3KeyboardIncrement属性,设置每按下一次箭头键时移动 GridSplitter 控件的距离。默认值为1

    VerticalAlignmentHorizontalAlignment,是用来控制splitter是水平的还是垂直的。默认情况下,HorizontalAlignmentRightVerticalAlignmentStretch,此时splitter位于格子的右边,移动splitter会左右移动它所在列的宽度分配。我们可以改变HorizontalAlignmentLeftCenter,而保持VerticalAlignmentStretch不变。

    我们可以通过splitterResizeBehavior枚举属性,改变上述行为,让我们选择哪个列会被splitter影响:

    CurrentAndNext     等效于splitter靠右

    PreviousAndCurrent等效于splitter靠左

    PreviousAndNext    等效于splitter居中

    BasedOnAlignment   等效于由splitter的HorizontalAlignment和VerticalAlignment来决定

    相应的,设置HorizontalAlignmentStretchVerticalAlignmentTopBottomCenter,从而使splitter为水平的。

    我们可以利用ResizeDirection属性,改变splitter影响的方向,有三种枚举值:Auto、Columns、Rows。对于水平splitter,按理说应该是上下移动,但是如果把ResizeDirection属性设为Columns

    则可以左右移动splitter,但是splitter的效果就看不到了。

    24GridSplitterRowColumn

    这个示例演示了让GridSplitter跨越行或列,这是一种比较合理的方式,从外观上:

              <GridSplitter Grid.Row="1"  Grid.ColumnSpan="3"

    25GroupBox

    GroupBox就是一个很简单的容器,用来盛放多个控件在其中,这样可以同时控制它们的共同属性,如GroupBox不可见时,则其中所有控件也都不再可见。

    26HeaderedContentControl

    HeaderedContentControl为包含单项内容并具有标头的所有控件提供基实现,有两个关键属性,ContentHeader属性,可设置为任何类型的对象。

    HeaderedContentControl具有有限的默认样式,其外观非常简单。如果希望增强控件的外观,可以创建新的ControlTemplate,如下:

                <Style x:Key="{x:Type HeaderedContentControl}" TargetType="{x:Type HeaderedContentControl}">

                    <Setter Property="Template">

                        <Setter.Value>

                            <ControlTemplate TargetType="{x:Type HeaderedContentControl}">

                                <StackPanel>

                                    <ContentPresenter ContentSource="Header"/>

                                    <ContentPresenter ContentSource="Content"/>

                                </StackPanel>

                            </ControlTemplate>

                        </Setter.Value>

                    </Setter>

                </Style>

    这就重写了所有的HeaderedContentControl控件,使用了ContentPresenter并且规定,Header在上,Content在下。

    在本例中,我们可以使用ContentControl来代替表示Content属性,也就是说,以下两种声明方式是相同的:

    //声明方式1

    <HeaderedContentControl HeaderTemplate="{StaticResource template2}" Header="Header"

    ContentTemplate="{StaticResource template3}" Content=" This is the content."/>

    //声明方式2

    <HeaderedContentControl HeaderTemplate="{StaticResource template2}" Header="Header">

          <ContentControl ContentTemplate="{StaticResource template3}"

               Content="This is the content" />

    </HeaderedContentControl>

    这里,我们还可以细化到为HeaderContent指定DataTemplate

                <DataTemplate x:Key="template2">

                    <TextBlock Text="{Binding}" Foreground="Green" FontSize="16"></TextBlock>

                </DataTemplate>

               <DataTemplate x:Key="template3">

                    <TextBlock Text="{Binding}" Foreground="Brown" FontSize="12"></TextBlock>

                </DataTemplate>

    注:有关数据模板的技术介绍参见“数据绑定”一章

    注:ExpanderGroupBoxTabItem派生于HeaderedContentControl

    再有就是,HeaderedContentControl有一个HasHeader只读属性,用来判断Header是否有值。

    27HeaderedItemsControl

    HeaderedItemsControl表示包含多个项并且具有标题的控件。类似于HeaderedContentControl,它的可视化样式也不多,需要手动创建数据模板和控件模板:

                <Style x:Key="{x:Type HeaderedItemsControl}"

            TargetType="{x:Type HeaderedItemsControl}">

                    <Setter Property="Template">

                        <Setter.Value>

                            <ControlTemplate TargetType="{x:Type HeaderedItemsControl}">

                                <StackPanel>

                                    <ContentPresenter ContentSource="Header"/>

                                    <ItemsPresenter/>

                                </StackPanel>

                            </ControlTemplate>

                        </Setter.Value>

                    </Setter>

                </Style>

    注:ItemsPresenter,在项控件的模板中使用,用于指定要将ItemsControl添加到控件的可视化树中的什么位置。

    相应的XAML中的控件语法:

                <HeaderedItemsControl Name="hitemsCtrl" HeaderTemplate="{StaticResource template1}" Header="Header">

                    <ItemsControl Name="ic1" ItemTemplate = "{StaticResource template4}" ItemsSource="{Binding Source={StaticResource Colors}}">

                    </ItemsControl>

                </HeaderedItemsControl>

    28Label

    先说LabelTarget属性:

    <TextBox Name="tb"/>

    <Label Target="{Binding ElementName=tb}">_File</Label>

     <TextBox Width="105" />

    通过为Label指定访问键并设置Target属性,可以向不支持访问键的元素TextBox提供键盘快捷键。通过在充当访问键的字符前面的紧邻位置放置一个下划线,可以指定标签的访问键。

    做一个实验,先定位在后面一个TextBox上,按Alt+F键,焦点就会跳到tb这个TextBox上。

    可以在Content属性中指定访问键,也可以通过将Content设置为AccessText对象来指定访问键:

    <Label Width="200" HorizontalAlignment="Left">

    <AccessText TextWrapping="WrapWithOverflow">_Another long piece goes here.</AccessText>

    </Label>

    还有就是Label不支持TextWrapping属性,为此需要在其内部嵌入一个TextBlock控件:

    <Label Width="200" HorizontalAlignment="Left">

    <TextBlock TextWrapping="WrapWithOverflow">A long piece of text that requires text wrapping

         goes here.</TextBlock>

    </Label>

    29ListBoxEvent

    这个示例演示了ListBox控件:

    1SelectionChanged事件,当改变ListBox选择项时触发,这里是将选中项显示在textbox上。

    2SelectionMode属性是一个枚举:

    Single            单选

    Multiple         多选,直接进行多选

    Extended        多选,需要同时按住ShiftCtrl键才能进行多选

    3ListBox中可以放置ListBoxItem

                <ListBox Name="lb" SelectionMode="Single">

                    <ListBoxItem>Item 1</ListBoxItem>

    也可以放置其它控件:

                <ListBox Width="265" Height="55" HorizontalAlignment="Left" SelectionMode="Multiple">

                    <DockPanel>

                        <Image Source="data"cat.png"/>

                        <TextBlock>CAT</TextBlock>

                    </DockPanel>

    30ListBoxHorizontal

    这个例子演示了横向排列的ListBox,而这个方向是在控件模板ListBoxTemplate中设置的——不能在ListBox中直接设置,有关控件模板的详细介绍见其它示例。

    31ListBoxItem

    这个示例根据右边菜单中选择的数字i——这是一个索引,将左边的ListBox[i]其显示在下方的按钮中。

    为此在XAML中:

                <MenuItem Header="Choose an Index Number">

                    <MenuItem Header="0" Click="GetIndex"/>

    相应的方法GetIndex,先通过e获取选择的数字:

                string item = ((MenuItem)sender).Header.ToString();

                int i = Convert.ToInt32(item);

    然后根据索引i获取这个Item,注意这个特殊的语句:

                ListBoxItem lbi = (ListBoxItem)

                    (lb.ItemContainerGenerator.ContainerFromIndex(i));

    32ListBoxItemStyle

    本例演示了在一个空StackPanel中动态添加ListBox,并在这个ListBox中添加两个Button,同时还指定了这个ListBox的样式:

                Style style = new Style(typeof(ListBoxItem));

                style.Setters.Add(new Setter(ListBoxItem.HorizontalContentAlignmentProperty,

                     HorizontalAlignment.Stretch));

                ListBox lb = new ListBox();

                lb.ItemContainerStyle = style;

    33ListBoxStyles

    这个示例演示了在XAML中使用StyleListBox添加样式。其中第三个最有特色,这是一个触发器,在鼠标移动到item上是,改变其背景色和其它属性:

        <Trigger Property="ListBoxItem.IsMouseOver" Value="true">

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

    34ListViewCode

    这个示例分别使用XAMLC#代码创建一个ListView数据绑定控件。

    ListView一般是这样的(需要数据绑定的配合):

                <ListView ItemsSource="{Binding Source={StaticResource EmployeeInfoDataSource}}">

                    <ListView.View>

                        <GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Employee Information">

                            <GridViewColumn DisplayMemberBinding="{Binding Path=FirstName}" Header="First Name" Width="100"/>

    相应的C#代码:

                ListView myListView = new ListView();

                GridView myGridView = new GridView();

                myGridView.AllowsColumnReorder = true;

                myGridView.ColumnHeaderToolTip = "Employee Information";

                GridViewColumn gvc1 = new GridViewColumn();

                gvc1.DisplayMemberBinding = new Binding("FirstName");

                gvc1.Header = "FirstName";

                gvc1.Width = 100;

                myGridView.Columns.Add(gvc1);

                myListView.ItemsSource = new myEmployees();

                myListView.View = myGridView;

                myStackPanel.Children.Add(myListView);

    35ListViewCheckBox

    这个例子演示了ListView的使用,这里我们关注的是如何在这个控件的模板中嵌入CheckBox,并让其与ListView具有相同的触发事件:

            <ListView

               SelectionChanged="mySelectionChanged">

                <ListView.View>

                    <GridView>

                        <GridViewColumn CellTemplate="{StaticResource FirstCell}"

                              Width="30"/>

    ListView指定了SelectionChanged事件,我们就是要让这个事件也适用于CheckBox被选中:

    这里,数据模板的第一列,FirstCell,对应一个控件模板,也就是一个包装了CheckBoxStackPanel,注意到其中CheckBox中的设定:

                    <CheckBox IsChecked="{Binding Path=IsSelected,

           RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}}"/>

    使用了相对绑定源,从而将CheckBoxIsChecked属性绑定到ListViewItemIsSelected属性

    36ListViewCustomView

    This sample shows how to create a ListView control that displays data in multiple view modes, which include the GridView and other custom view modes.

    37ListViewEditable

    这个示例演示了自定义一个可编辑的ListView控件。

    ListView的第三列,我们放置了自定义控件EditBox,并对其使用了数据模板,绑定它的Value属性到EmployeeNumber

              <GridViewColumn Header="ID" Width="50" >

                <GridViewColumn.CellTemplate>

                  <DataTemplate>

                    <l:EditBox Height="25" Value="{Binding Path=EmployeeNumber}" />

                  </DataTemplate>

                </GridViewColumn.CellTemplate>

              </GridViewColumn>

    为此,我们自定义了两个控件,EditBoxAdornerEditBox,前者派生于Adorner控件,后者派生于Cnntrol基类。在EditBox中引用了EditBoxAdorner控件。

    即使对于自定义控件,我们也可以对其使用控件模板,观察其中重要的一部分:

                        <ControlTemplate TargetType="{x:Type l:EditBox}">

                            <TextBlock x:Name="PART_TextBlockPart" 

                                 Text="{Binding Path=Value,RelativeSource =

                                       {RelativeSource TemplatedParent}}">

                            </TextBlock>

                        </ControlTemplate>

    注意这里:RelativeSource = {RelativeSource TemplatedParent}

    老实说,这句话我也没看明白,查了很多资料,不知道相对数据源到底定位在什么地方,TemplatedParent又是表示什么。

    注:自定义复杂的控件超出了本书一稿的范围,我会在以后补齐。

    38ListViewHeaderRoleStyle

    This sample shows how to create custom styles for a GridViewColumnHeader in a GridView by dragging the column header to a new position in a ListView control. This operation moves the header and its column to the new location.

    When you drag the GridViewColumnHeader during a move operation, the Role property of the header is automatically set to Floating and two versions of the header are displayed. One version is a partially transparent header that follows the mouse pointer during the move operation. The other version remains in the original position until you release the mouse button and the entire column and its header move to the location of the mouse pointer.

    This sample shows how to define a custom style for both header versions during a move operation.

    39ListViewItemStyle

    这个例子很多地方需要探讨。

    1)首先是使用到了内联数据源:

          <ListView.ItemsSource>

            <col:ArrayList>

              <sys:DateTime>2006/1/1</sys:DateTime>

              <sys:DateTime>2006/1/2</sys:DateTime>

            </col:ArrayList>

          </ListView.ItemsSource>

    这里colsys对应到了前面引入的命名空间:

          xmlns:sys="clr-namespace:System;assembly=mscorlib"

          xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"

    2)为ListView的列的头部定义了两个控件模板,以下是使用与未使用控件模板的效果图:

    右图中的YearMonth间的间隔线宽度为3,比左边要宽,这是在Thumb的控件模板中设置的。此外还有很多效果,都可以在名为GridViewColumnHeader的控件模板中找到,没有技术难点,这里不再多说。

    3ListView的列的头部GridViewColumnHeader,有一个Role枚举属性,

    40ListViewSort

    This sample shows how to create a ListView control that uses a GridView view mode to display dates. The sample enables sorting of data in ascending or descending order according to the contents of one column.

    为了在ListView中实现排序,

    41ListViewTemplate

    This sample shows how to create a ListView control that uses a GridView view mode to display a collection of DateTime objects. The sample defines the contents of column headers and cells by using templates.

    42GridViewWithGroups

    ListView中实现分组,要使用到CollectionViewSource,该标记是CollectionViewXAML中的表现。以下代码表示将数据源的Catalog属性,将其作为分组的依据:

        <CollectionViewSource x:Key='src'

                              Source="{Binding Source={StaticResource MyData}, XPath=Item}">

          <CollectionViewSource.GroupDescriptions>

            <PropertyGroupDescription PropertyName="@Catalog" />

          </CollectionViewSource.GroupDescriptions>

        </CollectionViewSource>

    注:更多关于CollectionViewSource的介绍,参见数据绑定一章

    然后,让ListView绑定到这个数据源,我们为ListViewGroupItem建立了样式和控件模板,内置了Expander才使得GroupItem具有可折叠性。

    注意到,Expander.Header的两个TextBlock,分别显示GroupItemNameItemCount,其中名称Name就是之前绑定的Catalog,而数量ItemCount则是根据这个Catalog统计得到的

                        <Expander.Header>

                          <DockPanel>

                            <TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>

                            <TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>

                          </DockPanel>

                        </Expander.Header>

    Expander.Header则显示之前绑定的条目,ItemsPresenter占位符表示GridView中的数据内容:

                        <Expander.Content>

                          <ItemsPresenter />

                        </Expander.Content>

    43MenuEvent

    这个例子演示了Menu的用法:

    <Menu>

       <MenuItem Header="_File">

              <MenuItem Header="_New" IsCheckable="true"/>

              <Separator/>

            <MenuItem Header="_Animals">

     <MenuItem.Header>

     <Image Source="data/cat.png"/>

     </MenuItem.Header>

    </MenuItem>

             <MenuItem Header="_Menu item with ToolTip">

              <MenuItem.ToolTip>

                <ToolTip>ToolTip Information. </ToolTip>

              </MenuItem.ToolTip>

             </MenuItem>

       </MenuItem>

    </Menu>

    使用MenuItem作为菜单项,不同级别的MenuItem可以进行嵌套,设置IsCheckable="true"使之有勾选的记号(默认为false)。Header中不光可以放置文本,也可以放Image,使用附属属性MenuItem.Header来实现。

    也可以使用InputGestureText设置快捷键:

              <MenuItem Header="_Cut" InputGestureText="Ctrl+X"/>

    最后要研究的是Command属性:

              <MenuItem Command="ApplicationCommands.Paste">

              <MenuItem.Header>_Open</MenuItem.Header>

              </MenuItem>

    创建一个MenuItem并将其Command属性设置为Paste命令。 没有将CommandTarget显式设置为TextBox对象。没有设置CommandTarget时,命令的目标为具有键盘焦点的元素。 如果具有键盘焦点的元素不支持Paste命令或者目前无法执行粘贴命令(例如剪贴板为空),则MenuItem将变灰。

    正如本例所示,基本设置了Command属性的MenuItem都是灰色的,因为这个简单的示例不支持诸如Open/New/Close等系统级别命令。

    更多Command信息请参见RoutedCommand挂钩技术。

    44MenuStyles

    这个示例演示了如何为Menu创建样式。

    关注一下如何为Separator重新定义样式:

            <Style x:Key="{x:Static MenuItem.SeparatorStyleKey}" TargetType="{x:Type Separator}">

                <Setter Property="OverridesDefaultStyle" Value="true" />

                <Setter Property="Template">

                    <Setter.Value>

                        <ControlTemplate TargetType="{x:Type Separator}">

                            <Image Width="50" Height="30" Source="data"separator.png"/>

                        </ControlTemplate>

                    </Setter.Value>

                </Setter>

           </Style>

    主要是定义了一个控件模板,使用图片取代了原先的横线。

    注:<Menu IsMainMenu="false" >

    IsMainMenu指示此Menu是否接收主菜单激活通知,主要用于一个页面有多个Menu,这时设置了IsMainMenu="false"Menu不再接受AltF10的激活通知。这个属性默认值为true

    45PopupSimple

    Popup控件具有独立的可视化外观:

              <Popup Name="myPopup">

                <Border BorderBrush="Beige" BorderThickness="1">

                 <TextBlock Name="myPopupText" Width="150" Background="LightBlue"

                   Foreground="Blue" TextWrapping="Wrap" />

                </Border>

              </Popup>

    可以把它放置在任何地方,本例是放在了Button中,那么Popup窗口就会显示在Button下方;如果放在Window下的StackPanel中,那么就会显示在应用程序下方。

    Popup有一个IsOpen属性,默认为false,就是不显示,需要触发机制,从而在后台将其改为true,显示在窗体中。

    注:这里有个问题没有解决,就是Popup控件显示后,必须使用语句设置其IsOpen属性为false,才能使其消失,而不能一段时间后自动消失,这个问题要使用DispatcherTimer才能解决,见下面的示例。

    46PopupPosition

    这个示例演示了2秒后Popup控件自动消失的技术,使用到了DispatcherTimer

            DispatcherTimer closeTimer = new DispatcherTimer(DispatcherPriority.Normal);

            private void OnStart(object sender, EventArgs e)

            {

                closeTimer.Interval = TimeSpan.FromSeconds(2);

                closeTimer.Tick += new EventHandler(TimeOutPopup);

                ColorBlue.IsChecked = true;

            }

            private void TimeOutPopup(object sender, EventArgs e)

            {

                myPopup.IsOpen = false;

                closeTimer.Stop();

            }

    语法类似于Timer,不再多说。

    47PopupCustomPlacement

    关于CustomPopupPlacementCallback委托的使用技术,参见示例66ToolTipCustom

    这里要说明的是,在placePopup回调方法中,返回了(-50, 90)的点,但由于在Popup控件中,设定为

            <Popup Name="myPopup" 

                  PlacementTarget ="{Binding ElementName=myButton}"

    也就是说,(-50, 90)是相对于Button的位置,而不是Grid中的位置(虽然Popup位于Grid中)。

    48PopupAnimated

    Popup的出现是取决于CheckBox的选中:

        <Popup IsOpen="{Binding ElementName=myCheckBox,Path=IsChecked}">

    为此,将PopupIsOpen属性绑定到了CheckBoxIsChecked属性。

    同时在Popup中,内嵌了一个带动画的Canvas,名为theTransform

           <Canvas.RenderTransform>

              <RotateTransform x:Name="theTransform" />

            </Canvas.RenderTransform>

    于是点击按钮后,Popup中的Canvas进行旋转:

                  <DoubleAnimation

                    Storyboard.TargetName="theTransform"

                    Storyboard.TargetProperty="(RotateTransform.Angle)"

                    From="0" To="360" Duration="0:0:5" AutoReverse="True"/>

    49ProgressBar

    这个例子演示了如何在StatusBar中添加一个ProgressBar,并设置这个ProgressBar的动画效果:

                ProgressBar progbar = new ProgressBar();

                progbar.IsIndeterminate = false;

                progbar.Orientation = Orientation.Horizontal;

                progbar.Width = 150;

                progbar.Height = 15;

                progbar.Background = Brushes.Gray;

                progbar.Foreground = Brushes.Red;

                sbar.Items.Add(progbar);

    效果如下图:

    我们可以设置它的宽和高、前景和背景,此外还有两个属性比较重要

    1)Orientation属性,决定了ProgressBar的方向,默认为水平的

    2)IsIndeterminate属性,默认为false,即进度条随着进度前进,只进行一遍;如果设为true,则循环显示进度条,直至进度结束,如下图所示:

    最后,为这个ProgressBar设计一个动画,使用它的BeginAnimation方法来添加这个动画效果:

                Duration duration = new Duration(TimeSpan.FromSeconds(10));

                DoubleAnimation doubleanimation = new DoubleAnimation(100.0, duration);

                progbar.BeginAnimation(ProgressBar.ValueProperty, doubleanimation);

    50RadioButton

    RadioButton的功能类似于传统意义Window中的单选按钮。可以没有组的概念:

    <RadioButton Name="rb" Click="WriteText">Click the radio button.</RadioButton>

    也可以把一组RadioButton聚合在一起,有两种方法:

    1)将一组RadioButton控件放入同一个父级,这里是StackPanel

    <StackPanel>     

        <RadioButton Name="rb1" Checked="WriteText2">Yes</RadioButton>

        <RadioButton Name="rb2" Checked="WriteText2">No</RadioButton>

        <RadioButton Name="rb3" Checked="WriteText2">No opinion</RadioButton>

    </StackPanel>

    2)设置每个RadioButton上的GroupName属性:

    <StackPanel>

        <RadioButton GroupName="colorgrp">Red</RadioButton>

        <RadioButton GroupName="colorgrp">Blue</RadioButton>

        <RadioButton GroupName="numgrp">1</RadioButton>

        <RadioButton GroupName="numgrp">2</RadioButton>

    </StackPanel>

    上述4RadioButton虽然在同一个StackPanel中,但是因为不同的GroupName被分为了两组。

    RadioButtonChecked事件,这与继承于ButtonBaseClick事件具有同样的效果。

    51RepeatButton

    RepeatButton表示从按下按钮到释放按钮的时间内重复引发其Click事件的控件。对于这个例子,鼠标点击按钮,在释放前持续触发事件,从而数字会一直增长——这是与Button唯一的区别。

    我们还可以控制RepeatButtonClick事件的发生时间和方式,Delay属性确定事件的开始时间。还可以通过Interval属性控制重复的间隔。

    52RepeatButtonStyles

    这个例子演示了RepeatButton的样式,和Button的样式使用方法是一样的。

    53ScrollBarTemplate

    This sample shows how to define a custom template for a ScrollBar control by styling its components, such as the Thumb and RepeatButton controls.

    这个例子演示了如何为ScrollBar定义一个控件模板,通过分别为ThumbRepeatButton定义了样式。

    54Slider

    This sample shows several simple examples of the Slider control and how to use its properties.

    这个例子演示了Slider的用法。

    讨论之前,让我们先介绍几个名词,来规范我们的用语,如下图,来自微软的MSDN

    Slider有横向和纵向两种,由其枚举属性Orientation决定:

          <Slider Value="0" Orientation="Horizontal"/>

    以上分别是Value="0"时,横向和纵向的Slider,其中Thumb默认分别位于TickBar的左边和底部。

    Slider控件有很多属性:

          <Slider Value="50" Orientation="Horizontal"

             IsSnapToTickEnabled="False" Maximum="9" TickPlacement="BottomRight"

             AutoToolTipPlacement="BottomRight" TickFrequency="3"

             AutoToolTipPrecision="2" IsDirectionReversed="True"

             IsMoveToPointEnabled="True"/>

    1Value值用来表示Thumb的初始位置,当这个值大于Maximum时,会使用Maximum作为初始位置,也就是位于终点。

    2)当我们把TickPlacement设为默认值None以外的值时,就会显示TickBar的刻度线,这里是:右下方BottomRight

    3AutoToolTipPlacement属性,设置在按下Thumb时是否显示包含Slider 的当前值的工具提示(此时为默认值None)。如果显示一个工具提示,则此属性还指定此工具提示的位置:BottomRightTopRight

    4AutoToolTipPrecision属性,设置在工具提示中的SliderValue的小数点右侧显示的小数位数,这里是保留2位小数。

    5IsDirectionReversed属性,设置增加值的方向。如果增加值的方向向左(对于水平滑块)或向下(对于垂直滑块),则为true;否则为false。默认值为false

    6IsMoveToPointEnabled属性,设置一个值,该值指示是否立即将SliderThumb移动到在鼠标指针悬停在Slider轨道的上方时鼠标单击的位置

    7TickFrequency属性,设置刻度线之间的间隔,这里是3。使TickFrequency属性生效的前提:必须将TickPlacement属性设置为一个None之外的值,才能让刻度线沿Slider显示。

    8IsSnapToTickEnabled属性,指示Slider是否自动将Thumb移动到最近的刻度线。

    再有就是下面Slider4个属性:

        <Slider

            Ticks="0, 1.1, 2.5, 3" IsSelectionRangeEnabled="true"

            SelectionStart="1.1" SelectionEnd="3"/>

    9Ticks属性,设置为Slider显示的刻度线的位置,使用如下:

    <Slider Ticks="0, 1.1, 2.5, 3"

    前提:这个属性也必须将TickPlacement属性设置为一个None之外的值,才能让刻度线沿Slider显示

    10IsSelectionRangeEnabled属性,指示Slider是否沿Slider显示选择范围,当这个值为true时,我们必须设置SelectionStartSelectionEnd属性,以显示选择范围

    55SliderStyles

    这个示例演示了如何为Slider应用样式。

    前面示例已经涉及,不再多说。

    56SliderwithDataBinding

    这个示例将SliderRectangle的宽、TextBlock的文本值绑定在一起——使用了BindingElementName

    57StatusBar

    StatusBar的布局方式同于其它控件——也需要设置Grid.Row等属性

    XAML中表现为——放置多个StatusBarItem,每个StatusBarItem都包装着一个元素:

            <StatusBar Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2">

                <StatusBarItem>

                    <TextBlock>Ready</TextBlock>

                </StatusBarItem>

                <StatusBarItem>

                    <Separator Style="{StaticResource StatusBarSeparatorStyle}"/>

                </StatusBarItem>

            </StatusBar>

    后台对应的C#代码为——一个Items集合:

                StatusBarItem sbi = new StatusBarItem();

                sbi.Content = dpanel;

                sbi.HorizontalAlignment = HorizontalAlignment.Right;

                sbar.Items.Add(sbi);

    当然,我们可以直接将元素加到Items集合中,而不用StatusBarItem

                TextBlock txtb = new TextBlock();

                txtb.Text = "Progress of download.";

                sbar.Items.Add(txtb);

    每次显示新的StatusBarItem,要事先使用sbar.Items.Clear();清空StatusBar中的所有元素,然后重新添加,从而实现状态栏的变化。

    注:这里我们设置了一个ProgressBar动画,在切换状态栏的时候,但是我们有看到应有的效果。后面再把它调试出来。

    58TabControl

    This example demonstrates how to create a tab control.

    这个示例演示了TabControl的用法。

    <TabControl TabStripPlacement="Top" Margin="0, 0, 0, 10">

     <TabItem Name="backgroundcolor" Header="Background">

            <TabItem.Content>Background property information goes here.</TabItem.Content>

     </TabItem>

     <TabItem Name="foregroundcolor" Header="Foreground">

            <TabItem.Content>Foreground property information goes here.</TabItem.Content>

     </TabItem>

    </TabControl>

    我们可以设置TabStripPlacement属性,来决定Tab出现的位置,有上下左右四个选项。

    TabControl是由若干TabItem组成的集合。

    59TabControlStyles

    This example demonstrates how to create tab controls with styles applied.

    这个示例演示了如何为TabControl及其TabItem应用样式。

    值得注意的是TabItemIsSelected属性,true表示当前被选中:

        <TabItem Header="Background" IsSelected="true" Style="{StaticResource Triggers}">

    60TabControlUsingControlTemplates

    This example shows how to style a TabControl.

    这个示例演示了如何为TabControl及其TabItem应用样式,尤其是设计了新的外观——使用了控件模板。

    TabControl可以简单表示为:

          <TabControl Width="250">

          <TabItem Header="One">

              <Ellipse Width="200" Height="200" Fill="#CABCAB"/>

          </TabItem>

          <TabItem Header="Two">

              <Ellipse Width="200" Height="200" Fill="#BADDF00D"/>

          </TabItem>

        </TabControl>

    为了看清楚两个控件模板的各自效果,我们采取每次注释掉其中一个的方法。

    1TabControl新的控件模板:

    可以看到,最外层是一个厚度1的黄色Border,紧接着是一个厚度3的红色BorderTabControl的背景色为LightBlue,在TabControl中放置了一个椭圆:

         <ContentPresenter ContentSource="SelectedContent"/>

    可以把ContentPresenter理解为一个占位符,它的ContentSource指向TabControl的一个属性,SelectedContent表示被选中TabItemContent,从上面的XAML中可以看到,这是一个Ellipse

    此外,ContentSource可以指向TabControl的任意属性,这个值都会显示在相应的TabControl中。

    注:仅当ContentPresenter位于模板中时,才应使用此属性。

    还有,就是TabPanel的使用:

    TabPanel用作TabControl中的TabItem的宿主。它决定TabItem的正确大小和位置,并处理多行TabItem对象的逻辑。这里,我们把TabPanel放置在Grid的第一行,也就是位于第二行的黄色Border的上方:

                 <TabPanel Grid.Row="0" IsItemsHost="true"/>

    IsItemsHost指示此Panel是由ItemsControl生成的用户界面(UI)项的容器,如果此Panel实例是项宿主,则为true

    TabItem新的控件模板:

    看上面的每个TabItem:先是绘制了背景为LightBlue、厚度为3的红色圆角Border,然后在上面写字,这些文本来源于TabItemHeader属性:

              <ContentPresenter ContentSource="Header">

    61TabControlwithContextMenu

    这个示例联合使用了TabControlContextMenu

        <TabItem Name="backgroundcolor" Header="Choose a Background Color">

            <TabItem.ContextMenu>

                <ContextMenu MenuItem.Click="MyMenuHandler">

                     <MenuItem Header="Red" Name="red"/>

                     <MenuItem Header="Blue" Name="red"/>

                </ContextMenu>

            </TabItem.ContextMenu>

            <TabItem.Content>Some content about background colors.</TabItem.Content>

        </TabItem>

    相应的MyMenuHandler方法(部分代码):

            void MyMenuHandler(object sender, RoutedEventArgs e)

            {

                if (e.Source == red)

                {

                    backgroundcolor.Background = Brushes.Red;

                    backgroundcolor.Header = "Background red";

                }

            }

    也就是说,根据eSource——在ContextMenu选中的MenuItemName名称,来设置TabItem对象backgroundcolor的属性

    62Thumb

    我们一般见到Thumb是在滚动条中,在WPF中也可以独立使用它,正如本例,使用一个Thumb手动控制TextBox的大小。

    <Thumb DragDelta="onDragDelta" DragStarted="onDragStarted" DragCompleted="onDragCompleted" />

    Thumb控件有三个事件,DragStartedDragCompleted分别在拖动开始与结束时触发,我们可以在期间加入一些自己的逻辑,正如示例所示,Thumb的背景色在拖动开始与结束时分别为不同的颜色。而DragStarted事件触发于Thumb的拖动,于是我们可以随之调整TextBox控件的大小。

    63ToolBarExample

    这个示例演示了TooBar控件的使用。

    1)基本使用如下:

            <ToolBarTray Background="White">

                <ToolBar Band="1" BandIndex="1">

                    <Button><Image Source="toolbargraphics"new.bmp" /></Button>

                    <Button><Image Source="toolbargraphics"open.bmp" /></Button>

                    <Button><Image Source="toolbargraphics"save.bmp" /></Button>

                    <Separator/>

                    <Button><Image Source="toolbargraphics"cut.bmp" /></Button>

                    <Button><Image Source="toolbargraphics"copy.bmp" /></Button>

                    <Button><Image Source="toolbargraphics"paste.bmp" /></Button>

                    <Separator/>

                    <Button><Image Source="toolbargraphics"print.bmp" /></Button>

                    <Button><Image Source="toolbargraphics"preview.bmp" /></Button>

               </ToolBar>

            </ToolBarTray>

    可以看到TooBar是使用ToolBarTray容器来承载的,为了好观察这个容器,我们为其指定蓝色的背景。ToolBarTray内允许防止多个TooBar控件。

    Separator是图中的分隔符。TooBar中的按钮一般使用Button,其中仅内嵌一个Image

    注意到TooBar控件的两个属性:BandBandIndex:分别标志了所在ToolBarTray的“行”和“列”,这里ToolBarTray中是可以有多个TooBar的,以下是部分代码:

    <ToolBarTray Background="White">

                <ToolBar Band="1" BandIndex="1">

                    <Button><Image Source="toolbargraphics"cut.bmp" /></Button>

               </ToolBar>

               <ToolBar Band="2" BandIndex="1">

                    <Button><Image Source="toolbargraphics"undo.bmp" /></Button>

               </ToolBar>

               <ToolBar Band="2" BandIndex="2">

                    <Button><Image Source="toolbargraphics"paint.bmp" /></Button>

               </ToolBar>

            </ToolBarTray>

    3)我们还可以在TooBar中放置下拉框ComboBox

         <ToolBar Band="1" BandIndex="1">

          <ComboBox Width="60">

            <ComboBoxItem FontWeight="Normal" IsSelected="True">Normal</ComboBoxItem>

            <ComboBoxItem FontWeight="Bold">Bold</ComboBoxItem>

            <ComboBoxItem FontWeight="Black">Black</ComboBoxItem>

          </ComboBox>

    4)还可以把TooBar“竖”起来显示,这时BandBandIndex也要起来计算:

    <ToolBarTray Background="White" Orientation="Vertical" Grid.RowSpan="2">

                <ToolBar Band="1" BandIndex="1">

                    <Button><Image Source="toolbargraphics"copy.bmp" /></Button>

                    <Button><Image Source="toolbargraphics"paste.bmp" /></Button>

               </ToolBar>

               <ToolBar Band="2" BandIndex="1" Grid.RowSpan="2">

                    <Button><Image Source="toolbargraphics"undo.bmp" /></Button>

                    <Button><Image Source="toolbargraphics"redo.bmp" /></Button>

               </ToolBar>

    </ToolBarTray>

    64ToolBarStyles

    这个例子演示了为TooBar使用样式。

    注意到第二个ToolBar,这里使用StackPanel来代替Seperator作为分隔符,为此我们在样式中指定了StackPanel的一些属性:

                    <StackPanel Style="{StaticResource Separator}"/>

    以及:

    <Style x:Key="Separator" TargetType="{x:Type StackPanel}">

        <Setter Property = "Margin" Value= "4"/>

        <Setter Property = "Width" Value= "2"/>

        <Setter Property = "Height" Value= "12"/>

        <Setter Property = "Background" Value= "Gray"/>

    </Style>

    这样就得到了自定义的分隔符,如下图:

    此外,还在ToolBar中同时放置了ExpanderComboBox,由于ToolBarTray的宽度不够——只有250,所以ComboBox需要点击才能显示出来,如图所示:

    65ToolTipAssign

    这个示例演示了如何将重复使用ToolTip的放到Resource中共享:

                   ToolTipService.ToolTip="{DynamicResource EllipseToolTip}"

    相应的Resource

        <ToolTip x:Key="EllipseToolTip" Content="An Ellipse"/>

    仔细看这两个Button

          <Button Name="TheTarget" Content="tooltip PlacementTarget"/>

          <Button ToolTip="{DynamicResource MyToolTip}"

                  Content="tooltip owner"

                  ToolTipService.Placement="Top"

                  ToolTipService.PlacementTarget="{Binding ElementName=TheTarget}"/>

    后面这个按钮将自身的PlacementTarget属性绑定在了前面的按钮上,由于同时设置了位置属性Placement="Top",所以ToolTip会显示在前面按钮的上方。

    注意到这个PlacementTarget属性的设置,它的后台代码如下:

                ToolTipService.SetPlacementTarget(tooltipOwner, targetButton);

    也就是说,一定要把这个拥有ToolTip的按钮tooltipOwner设置给目标元素——这里也是一个按钮targetButton

    66ToolTipCustom

    这个例子演示了如何指定并使用委托CustomPopupPlacementCallback,来定位ToolTip控件。为此我们使用Polygon定义了一个多边形myPolygon,并为其设置了ToolTip

                ToolTip aToolTip = new ToolTip();

                aToolTip.Placement = PlacementMode.Custom;

                aToolTip.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(placeToolTip);

                myPolygon.ToolTip = aToolTip;

    CustomPopupPlacementCallback委托返回根据PlacementTarget定义的可能点的数组。在显示ToolTip时,选中了一点来最大化可见ToolTip窗口的大小。

    让我们观察这个回调方法:

            public CustomPopupPlacement[] placeToolTip(Size popupSize, Size targetSize, Point offset)

            {

                CustomPopupPlacement[] ttplaces =

                   new CustomPopupPlacement[] { new CustomPopupPlacement() };

                ttplaces[0].Point = new Point(100, 280);

                ttplaces[0].PrimaryAxis = PopupPrimaryAxis.Horizontal;

                return ttplaces;

            }

    这个方法返回了TooTip停靠的位置

    我们可以改写这个回调方法,让其返回两个CustomPopupPlacement对象。如果ToolTip在第一个位置被屏幕边缘隐藏,则ToolTip将放在第二个位置。

            public CustomPopupPlacement[] placeToolTip2(Size popupSize, Size targetSize, Point offset)

            {

                CustomPopupPlacement placement1 = new CustomPopupPlacement(new Point(-50, 100), PopupPrimaryAxis.Vertical);

                CustomPopupPlacement placement2 = new CustomPopupPlacement(new Point(10, 20), PopupPrimaryAxis.Horizontal);

               

                return new CustomPopupPlacement[] { placement1, placement2 };

            }

    图左的ToolTip,虽然位于窗体外边(-50, 100),但是由于窗体在屏幕中间位置,所以即使ToolTip越界,也会显示出来;而图右的ToolTip,本来首选窗体外边(-50, 100),但是由于窗体左边与屏幕左边对齐,所以这个点被隐藏了,所以采取第二个点(10, 20)显示。最后如果所有的点都在屏幕外,则WPF会自动选取一个最佳点来显示TooTip

    注:Placement属性必须设置为Custom才能使用回调委托。

    67ToolTipEvents

    ToolTip有两个事件OpenedClosed,分别在ToolTip打开和关闭时触发。

    ToolTip有两个属性,成对出现:

                     PlacementRectangle="50,50,0,0"

                     Placement="Bottom"

    表示ToolTip所在的矩形的尺寸,以及ToolTip的文本在矩形中的位置。

    68ToolTipService

    This sample shows how to define tooltips by using the ToolTip and ToolTipService controls. The sample provides examples of both methods in XAML and in code. It also specifies the timing properties that the ToolTipService provides.

    ToolTipService是一个静态类,表示一个服务,该服务提供用于控制工具提示的显示和行为的属性和事件。

    ToolTipService一般用来设置ToolTip的附属属性而出现在其他控件中。比如说以下XAML

          <EllipseToolTipService.Placement="Right" >

    这与以下XAML的效果是一样的:

          <Ellipse >

            <Ellipse.ToolTip>

             <ToolTip Placement="Right"

    ToolTip 类和 ToolTipService 类共享用于自定义工具提示的许多相同属性。如果同时设置了等效的 ToolTip ToolTipService 属性,则 ToolTipService 属性优先。

    只有以下三个属性是ToolTipService所特有的:

    InitialShowDelay

    ShowDuration

    BetweenShowDelay

    此外,由于ToolTipService这些都是依赖属性,所以都使用GetXXXSetXXX方法进行操作,如:

                bool myBool = ToolTipService.GetHasDropShadow(

                             (DependencyObject)FindName("ellipse2"));

                ToolTipService.SetHasDropShadow(ellipse2, false);

    69ToolTipSimple

    这个例子演示了ToolTip的多种用法。

    1)作为一个附属属性,如TextBox.ToolTip,其中不光可以放置TextBlock,也可以放置Image

    <TextBox HorizontalAlignment="Left" ToolTipService.InitialShowDelay="1500" >

    TextBox with ToolTip

         <TextBox.ToolTip>

    <TextBlock>Useful information goes here.</TextBlock>

         <Image Source="data"flower.jpg"/>

         </TextBox.ToolTip>

    </TextBox>

    在上面的XAML中,我们可以延迟ToolTip的显示,比如说1.5秒之后。

    对于disabledButton而言,是不支持ToolTip的,为此,我们需要额外设置属性ToolTipService.ShowOnDisabled

        <Button IsEnabled="false" ToolTipService.ShowOnDisabled="True"

                Content="Disabled Button">

    2ToolTip的后台代码创建:

                tt = new ToolTip();

                tt.Content = "Created with C#";

                button.ToolTip = tt;

    需要有宿主如Button,单独存在的ToolTip没有意思。

    70ToolTipStyles

    我们可以为ToolTip设置样式:

           <ToolTip Style="{StaticResource Simple}">

    可以在App.xaml中找到这个Style。其中ToolTip的位移效果由以下样式决定:

                <Setter Property = "HorizontalOffset" Value="50"/>

                <Setter Property = "VerticalOffset" Value="50"/>

    以下是鼠标移动到TextBox上的ToolTip效果:

    71TreeListView

    This sample shows how to create a custom view that resembles the GridView view mode that is used by ListView. The custom view presents hierarchical data in columns at each level of the TreeView.

    In this sample, the TreeListView class inherits from the TreeView class and the ControlTemplate definitions for its header and items use the GridViewColumnCollection, GridViewRowPresenter, and GridViewHeaderRowPresenter classes to specify how the TreeListView appears.

    72TreeViewSelectedValue

    这个示例简单的数据绑定一个TreeView,可以通过将数据源设置给它的SelectedValuePath属性,来决定在TreeView中显示什么:

            <TreeView SelectedValuePath="EmployeeNumber" ItemsSource="

    而选择一个Item后,TextBlock的显示,则相对绑定到了SelectedValuePath上:

    <TextBlock Text="{Binding ElementName=myTreeView, Path=SelectedValuePath}" />

    73TreeViewSimple

    这个例子演示了TreeView的多种创建方法.

    1)基本方式:

            <TreeView>

                <TreeViewItem Header="Employee1">

                    <TreeViewItem Header="Jesper"/>

    只要你愿意,使用TreeViewItem嵌套,可以无限级别创建到N层。

    2TreeViewItem中可以放置任意多各种元素,如ButtonDockPanelImage等:

                <TreeViewItem Header ="Employee1">

                    <TreeViewItem.Items>

                        <Button>Jesper</Button>

                        <DockPanel>

                            <Image Source="data"dog.png"/>

                            <TextBlock Margin="5" Foreground="Brown"

                           FontSize="12">Dog</TextBlock>

                        </DockPanel>

                    </TreeViewItem.Items>

                </TreeViewItem>

    3)可以把TreeViewItemHeader属性也设置为Button等元素:

                    <TreeViewItem.Header>

                        <Button>Employee1</Button>

                    </TreeViewItem.Header>

    4)可以对TreeView直接进行数据绑定:

            <TreeView ItemsSource="{Binding Source={StaticResource myEmployeeData},

            XPath=EmployeeInfo}"/>

  • 相关阅读:
    PHP打开错误提示和关闭错误提示的方法
    squid的简单介绍
    伪静态与重定向--RewriteRule
    PHP操作Redis常用技巧总结
    爱漂泊人生 30个php操作redis常用方法代码例子
    Mysql与Redis的同步实践
    Linux 命令之grep
    Linux 命令之sed
    Linux shell 计算两个文件的并集、交集、差集
    Linux 软链接的创建、删除和更新
  • 原文地址:https://www.cnblogs.com/Jax/p/1194218.html
Copyright © 2020-2023  润新知