控件

总览:

按钮 (Buttons)
  • Button:基本按钮控件,用户点击时触发操作或事件。
  • RepeatButton:类似于 Button,但按住不放时会持续触发点击事件,适合需要重复输入的情况。
数据显示 (Data Display)
  • DataGrid:强大的数据表格控件,用于显示和编辑数据的表格形式,支持排序、过滤和分组。
  • ListView:用于显示数据列表,支持自定义布局和排序,常用于显示数据集合。
  • TreeView:用于显示树形结构的数据,比如文件夹和文件的层次结构。
日期显示和选择 (Date Display and Selection)
  • Calendar:显示月份的日历控件,用户可以选择一个或多个日期。
  • DatePicker:允许用户从日历中选择日期,通常用于表单输入。
对话框 (Dialog Boxes)
  • OpenFileDialog:用于让用户选择一个或多个文件的对话框。
  • PrintDialog:用于配置打印设置并发送文档到打印机的对话框。
  • SaveFileDialog:让用户选择保存文件路径和文件名的对话框。
数字墨水 (Digital Ink)
  • InkCanvas:允许用户通过触控或手写笔在其上绘制和书写的画布控件。
  • InkPresenter:用于呈现和处理数字墨水数据,通常与 InkCanvas 一起使用。
文档 (Documents)
  • DocumentViewer:用于查看文档(如 XPS 文件)的控件。
  • FlowDocumentPageViewer:以分页形式显示文档的控件,适合较长文档的阅读。
  • FlowDocumentReader:允许用户以多种布局模式查看文档的控件。
  • FlowDocumentScrollViewer:类似于 ScrollViewer,用于滚动查看长文档。
  • StickyNoteControl:用于添加和显示粘性便签,通常用于注释文档。
输入 (Input)
  • TextBox:用于接受用户文本输入的基本控件。
  • RichTextBox:类似于 TextBox,但支持富文本格式(如粗体、斜体等)的输入和编辑。
  • PasswordBox:专门用于输入密码的控件,输入内容以掩码字符显示。
布局 (Layout)
  • Border:为子元素提供边框的简单控件。
  • Canvas:允许子元素自由定位的容器,不受布局规则约束。
  • DockPanel:将子元素停靠在容器的边缘,未停靠的元素占据剩余空间。
  • Grid:网格布局,子元素根据行和列排列。
  • StackPanel:将子元素垂直或水平堆叠排列的容器。
  • WrapPanel:将子元素从左到右依次排列,当一行放不下时换行排列。
媒体 (Media)
  • Image:用于显示图像的控件。
  • MediaElement:用于播放音频和视频内容的控件。
  • SoundPlayerAction:允许在响应某些事件时播放声音的控件。
菜单 (Menus)
  • ContextMenu:为元素提供右键菜单的控件。
  • Menu:提供菜单栏,常用于应用程序的主菜单。
  • ToolBar:工具栏控件,通常包含一系列按钮和操作。
导航 (Navigation)
  • Frame:用于导航和显示内容的控件,通常与导航结构结合使用。
  • Hyperlink:文本链接控件,用户点击后可以导航到指定 URL 或其他内容。
  • Page:独立的页面控件,通常在 Frame 或 NavigationWindow 中使用。
  • NavigationWindow:支持导航功能的窗口控件,类似于浏览器窗口。
  • TabControl:标签页控件,允许在不同选项卡之间切换显示内容。
选择 (Selection)
  • CheckBox:允许用户进行二元选择(勾选或未勾选)的控件。
  • ComboBox:下拉列表控件,用户可以从中选择一个选项。
  • ListBox:类似于 ComboBox,但显示的是完整的选项列表,用户可以选择一个或多个项。
  • RadioButton:一组单选按钮,用户在一组中只能选择一个。
  • Slider:滑块控件,用户可以通过拖动滑块选择一个范围内的值。
用户信息 (User Information)
  • AccessText:用于显示带有加速键(快捷键)的文本的控件。
  • Label:用于显示静态文本的简单控件,通常与其他控件结合使用。
  • Popup:弹出控件,显示在其他内容之上的临时消息或界面。
  • ProgressBar:进度条控件,用于显示任务的完成进度。
  • StatusBar:通常用于显示应用程序状态信息的底部条状控件。
  • TextBlock:用于显示文本的轻量级控件,适合较小文本块。
  • ToolTip:为其他控件提供工具提示的控件,当用户悬停在控件上时显示附加信息。

通用设置

动态调整控件大小:

可以使用 MinHeightMaxHeightMinWidthMaxWidth 来限制控件的大小,并使用 HorizontalAlignmentVerticalAlignment 来控制对齐方式。

<Button Content="调整大小的按钮" 
        Height="100" 
        Width="200" 
        MinHeight="50" 
        MaxHeight="150" 
        HorizontalAlignment="Center" />

根据控件内部实际使用空间自由填充:

可以通过设置 GridHeight="Auto"Width="Auto",使得控件根据其内容自动调整大小。

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

    <TextBlock Grid.Row="0" Text="这个控件的高度会根据内容自动调整" />
    <Button Grid.Row="1" Content="这个按钮会占用剩余空间" />
</Grid>

字体设置

<TextBlock Text="组合字体样式" 
           FontWeight="Bold" 
           FontStyle="Italic" 
           FontSize="20" 
           Foreground="Blue" 
           FontFamily="Times New Roman" />

不同部分使用不同样式:
<TextBlock>
<Run Text="加粗的部分" FontWeight="Bold" />
<Run Text="普通字体" />
<Run Text="斜体部分" FontStyle="Italic" />
</TextBlock>
TextWrapping
控制文本内容是否在控件宽度超出时换行。
NoWrap:不换行,文本会溢出控件。
Wrap:自动换行,当文本超过控件宽度时自动换行。
WrapWithOverflow:换行时允许溢出部分文本。

<TextBlock Text="This is a very long text" TextWrapping="Wrap"/> <!-- 文本超过控件宽度时换行 -->
<TextBox Text="This is another long text" TextWrapping="WrapWithOverflow"/> <!-- 换行但允许溢出 -->

对齐

Margin

用于设置控件与其周围元素之间的间距。
Margin="left,top,right,bottom",也可以简写为一个或两个值。

  • 四个值表示四个方向的间距。
  • 两个值时,第一个值是左右,第二个是上下。
  • 一个值表示四个方向的间距相同。
<Button Content="Click Me" Margin="10"/> <!-- 四个方向都是10px -->
<Button Content="Click Me" Margin="10,20"/> <!-- 左右10px,上下20px -->
<Button Content="Click Me" Margin="5,10,15,20"/> <!-- 分别设置左、上、右、下的边距 -->
HorizontalAlignment

控制整个控件在其父容器中的水平对齐方式(控件本身的位置),适用于所有 UI 元素
Left:控件左对齐。
Center:控件水平居中对齐。
Right:控件右对齐。
Stretch:控件在父容器中水平拉伸填充。

<Button Content="Submit" HorizontalAlignment="Right" Width="100"/> <!-- 按钮在父容器中右对齐 -->
HorizontalContentAlignment

制控件内的内容(如文本、子控件)在水平方向上的对齐方式,常用于如 Button、TextBox、Label 等控件。
可用的和HorizontalAlignment一致,也是那4个

<Button Content="Submit" HorizontalContentAlignment="Center"/> <!-- 按钮内的文本居中 -->

滚动条

HorizontalScrollBarVisibility
控制是否显示水平滚动条,常用于 TextBox、ScrollViewer。
Disabled:禁用水平滚动条,内容超出也不会滚动。
Auto:当内容超出可视区域时自动显示水平滚动条。
Hidden:隐藏水平滚动条,但内容依然可以滚动。
Visible:始终显示水平滚动条,无论内容是否超出

<TextBox HorizontalScrollBarVisibility="Auto" TextWrapping="NoWrap" Width="100" Height="50"/> <!-- 当文本超过宽度时显示滚动条 -->

Button(按钮)

按钮是用户界面中最常见的交互控件之一,用于触发事件或命令。

<Button Content="Click Me" Width="100" Height="50" Click="Button_Click"/>
myButton.IsEnabled = false; // 禁用
myButton.IsEnabled = true;  // 启用
<Button Content="Click Me" Visibility="Collapsed"/>

Visibility 属性的可选值有:

  • Visible: 控件可见
  • Collapsed: 控件不可见,并且不占用布局空间
  • Hidden: 控件不可见,但仍占用布局空间

2. TextBox(文本框)

文本框允许用户输入和编辑文本。

<TextBox Width="200" Height="30" Text="Enter text here" />

TextBlock(文本块)

文本块用于显示只读文本。只显示文本,没有交互功能,性能较好

<TextBlock Text="Hello, World!" FontSize="16" FontWeight="Bold" />

Label(标签)

标签用于显示描述性文本,一般用于表单标签。具备为控件指定焦点的功能,并且可以包含更多复杂的内容如按钮,图标等

<Label Content="Name:" />

CheckBox(复选框)

复选框允许用户选择或取消选择一个选项。

<CheckBox Content="Accept Terms and Conditions" IsChecked="False"/>

RadioButton(单选按钮)

单选按钮允许用户在一组选项中选择一个。

<StackPanel>
    <RadioButton GroupName="Colors" Content="Red" IsChecked="True" />
    <RadioButton GroupName="Colors" Content="Green" />
    <RadioButton GroupName="Colors" Content="Blue" />
</StackPanel>

数据绑定:

public class Option : INotifyPropertyChanged
{
    private string name;
    private bool isSelected;

    public string Name
    {
        get => name;
        set
        {
            name = value;
            OnPropertyChanged(nameof(Name));
        }
    }

    public bool IsSelected
    {
        get => isSelected;
        set
        {
            isSelected = value;
            OnPropertyChanged(nameof(IsSelected));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
public class Option : INotifyPropertyChanged
{
    private string name;
    private bool isSelected;

    public string Name
    {
        get => name;
        set
        {
            name = value;
            OnPropertyChanged(nameof(Name));
        }
    }

    public bool IsSelected
    {
        get => isSelected;
        set
        {
            isSelected = value;
            OnPropertyChanged(nameof(IsSelected));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ItemsControl ItemsSource="{Binding Options}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <RadioButton Content="{Binding Name}" 
                                 IsChecked="{Binding IsSelected, Mode=TwoWay}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>


ComboBox(组合框)

组合框允许用户从下拉列表中选择一个项。

<ComboBox Width="200" SelectedIndex="1">
    <ComboBoxItem Content="Option 1" />
    <ComboBoxItem Content="Option 2" />
    <ComboBoxItem Content="Option 3" />
</ComboBox>
myComboBox.SelectedItem = myComboBox.Items[1]; // 设置第二项为默认选中项

数据绑定:

public partial class MainWindow : Window
{
    public ObservableCollection<string> Items { get; set; }
    private string selectedItem;

    public string SelectedItem
    {
        get => selectedItem;
        set
        {
            selectedItem = value;
            OnPropertyChanged(nameof(SelectedItem));
        }
    }

    public MainWindow()
    {
        InitializeComponent();

        Items = new ObservableCollection<string> { "Item 1", "Item 2", "Item 3" };
        SelectedItem = Items[1]; // 默认选择第二项

        this.DataContext = this;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ComboBox ItemsSource="{Binding Items}"
                  SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>
    </Grid>
</Window>


ListBox(列表框)

列表框用于显示一系列项,用户可以选择其中一个或多个项。

<ListBox Width="200" Height="100">
    <ListBoxItem Content="Item 1" />
    <ListBoxItem Content="Item 2" />
    <ListBoxItem Content="Item 3" />
</ListBox>

数据绑定:

public class ListItem : INotifyPropertyChanged
{
    private string name;
    private bool isEnabled;
    private bool isSelected;

    public string Name
    {
        get => name;
        set
        {
            name = value;
            OnPropertyChanged(nameof(Name));
        }
    }

    public bool IsEnabled
    {
        get => isEnabled;
        set
        {
            isEnabled = value;
            OnPropertyChanged(nameof(IsEnabled));
        }
    }

    public bool IsSelected
    {
        get => isSelected;
        set
        {
            isSelected = value;
            OnPropertyChanged(nameof(IsSelected));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public partial class MainWindow : Window
{
    public ObservableCollection<ListItem> ListItems { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        ListItems = new ObservableCollection<ListItem>
        {
            new ListItem { Name = "Item 1", IsEnabled = true },
            new ListItem { Name = "Item 2", IsEnabled = false }, // 这个项是禁用的
            new ListItem { Name = "Item 3", IsEnabled = true }
        };

        this.DataContext = this;
    }
}

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox ItemsSource="{Binding ListItems}" 
                 SelectionMode="Multiple">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </Grid>
</Window>

 <ListBox Grid.Row="1" x:Name="lbApplist" ItemsSource="{Binding MonitoredAppList}"
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel IsItemsHost="True"></WrapPanel>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <ContentControl>
                        <local:MonitoredAppConfigControl ></local:MonitoredAppConfigControl>
                    </ContentControl>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
<ListBox Width="200" Height="100" ItemsSource="{Binding People}" DisplayMemberPath="Name">

TreeView(树形结构)

用于显示具有层次结构的数据。

<Window x:Class="TreeViewExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TreeView 示例" Height="350" Width="525">
    <Grid>
        <TreeView Name="fileTreeView">
            <!-- 根节点 -->
            <TreeViewItem Header="C:\" IsExpanded="True">
                <!-- 子节点 -->
                <TreeViewItem Header="Program Files" IsExpanded="True">
                    <!-- 子节点的子节点 -->
                    <TreeViewItem Header="Microsoft Office" />
                    <TreeViewItem Header="Visual Studio" />
                </TreeViewItem>
                <TreeViewItem Header="Users" IsExpanded="True">
                    <TreeViewItem Header="Public" />
                    <TreeViewItem Header="User1" />
                </TreeViewItem>
                <TreeViewItem Header="Windows" />
            </TreeViewItem>
        </TreeView>
    </Grid>
</Window>

绑定:

public class FileSystemItem
{
    public string Name { get; set; }
    public ObservableCollection<FileSystemItem> Children { get; set; }
    public bool IsFolder { get; set; }

    public FileSystemItem(string name, bool isFolder)
    {
        Name = name;
        IsFolder = isFolder;
        Children = new ObservableCollection<FileSystemItem>();
    }
}

<Window x:Class="TreeViewExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TreeViewExample"
        Title="TreeView 示例" Height="350" Width="525">
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>
    <Grid>
        <TreeView ItemsSource="{Binding FileSystemItems}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

//初始化数据
public class MainViewModel
{
    public ObservableCollection<FileSystemItem> FileSystemItems { get; set; }

    public MainViewModel()
    {
        // 创建根节点
        var root = new FileSystemItem("C:\\", true);

        // 添加子节点
        var programFiles = new FileSystemItem("Program Files", true);
        programFiles.Children.Add(new FileSystemItem("Microsoft Office", false));
        programFiles.Children.Add(new FileSystemItem("Visual Studio", false));
        
        var users = new FileSystemItem("Users", true);
        users.Children.Add(new FileSystemItem("Public", false));
        users.Children.Add(new FileSystemItem("User1", false));

        root.Children.Add(programFiles);
        root.Children.Add(users);
        root.Children.Add(new FileSystemItem("Windows", true));

        // 将根节点添加到集合中
        FileSystemItems = new ObservableCollection<FileSystemItem> { root };
    }
}


Slider(滑块)

滑块允许用户在一个范围内选择一个值。

<Slider Minimum="0" Maximum="100" Value="50" Width="200" />

ProgressBar(进度条)

进度条用于显示操作的进度。

<ProgressBar Minimum="0" Maximum="100" Value="70" Width="200" Height="20" />

Image(图像)

图像控件用于显示图片。

<Image Source="image.jpg" Width="100" Height="100"/>

Grid(网格)

Grid是一个常用的布局控件,用于创建行和列的布局。

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" /> <!-- 按照控件内部占用空间,自动调整大小 -->
        <RowDefinition Height="*" /> <!-- 占用所有剩余空间 (所有包含*的按照比例占满剩余空间) -->
         <RowDefinition Height="2*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    
    <Label Content="Name:" Grid.Row="0" Grid.Column="0" />
    <TextBox Grid.Row="0" Grid.Column="1" />
    <Button Content="Submit" Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Center" /> <!--Column Span表示占几列 -->
</Grid>

StackPanel(堆叠面板)

StackPanel是另一个常用的布局控件,它将子元素按顺序垂直或水平排列。默认是垂直排列

<StackPanel Orientation="Vertical">
    <TextBlock Text="First Item" />
    <TextBlock Text="Second Item" />
    <TextBlock Text="Third Item" />
    <TextBlock Text="这个StackPanel是垂直排列的" />
    <StackPanel Orientation="Horizontal">
            <Button Content="水平按钮 1" />
            <Button Content="水平按钮 2" />
        </StackPanel>
</StackPanel>

DockPanel(停靠面板)

DockPanel允许子元素停靠在其边缘(顶部、底部、左侧、右侧)或填充剩余空间。

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

Canvas(画布)

Canvas提供了一个绝对定位的布局控件,可以在其上使用坐标指定子元素的位置。

<Canvas>
    <Button Content="Click Me" Canvas.Left="50" Canvas.Top="30" />
    <TextBox Canvas.Left="50" Canvas.Top="70" Width="100" />
</Canvas>

TabControl(选项卡控件)

TabControl允许用户在多个选项卡页面之间切换。

<TabControl>
    <TabItem Header="Tab 1">
        <TextBlock Text="Content for Tab 1" />
    </TabItem>
    <TabItem Header="Tab 2">
        <TextBlock Text="Content for Tab 2" />
    </TabItem>
</TabControl>

ToolTip

ToolTip 是 WPF 中用于在用户悬停控件时显示的附加信息提示。它可以附加到大多数控件上。

<Button Content="悬停我看看">
    <Button.ToolTip>
        <ToolTip Content="这是一个提示信息" />
    </Button.ToolTip>
</Button>

支持复杂内容

<Button Content="悬停查看更多">
    <Button.ToolTip>
        <ToolTip>
            <StackPanel>
                <TextBlock Text="提示信息:" FontWeight="Bold"/>
                <TextBlock Text="这是详细的提示信息" />
                <Image Source="image.jpg" Width="50" Height="50"/>
            </StackPanel>
        </ToolTip>
    </Button.ToolTip>
</Button>

DataGrid

DataGrid 用于显示表格数据,支持排序、过滤、分组以及可编辑的列。它非常适合展示结构化的数据,比如数据库表或列表。

<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding YourDataCollection}" />
  • AutoGenerateColumns="True":自动生成列,根据绑定的数据的属性名称。
  • ItemsSource:绑定的数据源(通常是集合,如 ObservableCollectionList)。

你可以自定义列,比如文本列、复选框列、模板列等:

<DataGrid ItemsSource="{Binding YourDataCollection}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
        <DataGridCheckBoxColumn Header="Is Active" Binding="{Binding IsActive}" />
        <DataGridTemplateColumn Header="Custom Template">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" />
                        <Button Content="Click" Command="{Binding CustomCommand}" />
                    </StackPanel>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>
  • 排序:默认支持列排序,点击列头可切换升序和降序。
  • 分页:通过外部逻辑实现分页,例如使用 ObservableCollection 控制页面的数据量。
  • 编辑功能:将 DataGrid 设置为可编辑,如果需要更好的控制对象的编辑流程,还绑定 IEditableObject 接口,这样可以控制对象的编辑,取消和提交,管理编辑对象的生命周期。
<Window x:Class="WPFDataGridExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DataGrid 编辑示例" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="dataGrid" 
                  AutoGenerateColumns="False" 
                  ItemsSource="{Binding Persons}" 
                  CanUserAddRows="True"
                  CanUserDeleteRows="True" 
                  CanUserSortColumns="True" 
                  IsReadOnly="False" 
                  Margin="10" 
                  CellEditEnding="DataGrid_CellEditEnding">
            <DataGrid.Columns>
                <DataGridTextColumn Header="姓名" Binding="{Binding Name, Mode=TwoWay}" />
                <DataGridTextColumn Header="年龄" Binding="{Binding Age, Mode=TwoWay}" />
                <DataGridCheckBoxColumn Header="是否激活" Binding="{Binding IsActive, Mode=TwoWay}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>
  • AutoGenerateColumns="False":我们手动定义列,不自动生成。
  • CanUserAddRows="True":允许用户添加行。
  • CanUserDeleteRows="True":允许用户删除行。
  • IsReadOnly="False":允许编辑单元格。
  • CellEditEnding="DataGrid_CellEditEnding":在编辑完成时触发的事件,用于处理保存逻辑。
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace WPFDataGridExample
{
    public class Person : INotifyPropertyChanged
    {
        private string name;
        private int age;
        private bool isActive;

        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                OnPropertyChanged("Name");
            }
        }

        public int Age
        {
            get { return age; }
            set
            {
                age = value;
                OnPropertyChanged("Age");
            }
        }

        public bool IsActive
        {
            get { return isActive; }
            set
            {
                isActive = value;
                OnPropertyChanged("IsActive");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class MainViewModel
    {
        public ObservableCollection<Person> Persons { get; set; }

        public MainViewModel()
        {
            Persons = new ObservableCollection<Person>
            {
                new Person { Name = "Alice", Age = 30, IsActive = true },
                new Person { Name = "Bob", Age = 25, IsActive = false },
                new Person { Name = "Charlie", Age = 40, IsActive = true }
            };
        }
    }
}
using System.Windows;

namespace WPFDataGridExample
{
    public partial class MainWindow : Window
    {
        private MainViewModel viewModel;

        public MainWindow()
        {
            InitializeComponent();
            viewModel = new MainViewModel();
            this.DataContext = viewModel;
        }

        private void DataGrid_CellEditEnding(object sender, 
                                            System.Windows.Controls.DataGridCellEditEndingEventArgs e)
        {
            // 在这里处理单元格编辑完成后的逻辑,比如保存更改
            var editedPerson = e.Row.Item as Person;
            MessageBox.Show($"你刚刚编辑了:{editedPerson.Name}, 年龄:{editedPerson.Age}, 是否激活:{editedPerson.IsActive}");
        }
    }
}
  • MainViewModel 是窗口的数据上下文(DataContext),包含一个 ObservableCollection<Person>,用于存储和显示表格中的数据。

  • CellEditEnding 事件在编辑单元格时触发,显示编辑后的数据或处理其他逻辑。

  • 你可以直接在 DataGrid 中编辑 NameAgeIsActive 列的值。

  • 可以通过点击表格最下方的空白行添加新的数据。

  • 用户可以通过右键菜单或选择行按 Delete 键删除行。


ListView

用于显示列表数据。

<ListView ItemsSource="{Binding YourDataCollection}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
            <GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}" />
        </GridView>
    </ListView.View>
</ListView>

使用 DataTemplate 可以自定义 ListView 中每一项的外观:

<ListView ItemsSource="{Binding YourDataCollection}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" />
                <TextBlock Text=" - " />
                <TextBlock Text="{Binding Age}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
  • 选择模式:通过 SelectionMode 属性设置单选、多选。
  • 分组:通过 CollectionViewSource 实现分组展示。
  • 排序和过滤:通过 CollectionView 实现数据的排序和过滤。

ContextMenu

的右键菜单,通常用于为用户提供上下文相关的操作。

<Button Content="右键点击我">
    <Button.ContextMenu>
        <ContextMenu>
            <MenuItem Header="操作1" />
            <MenuItem Header="操作2" />
        </ContextMenu>
    </Button.ContextMenu>
</Button>

ContextMenu 项目可以绑定命令,以响应用户点击。

<Button Content="右键点击我">
    <Button.ContextMenu>
        <ContextMenu>
            <MenuItem Header="删除" Command="{Binding DeleteCommand}" />
            <MenuItem Header="编辑" Command="{Binding EditCommand}" />
        </ContextMenu>
    </Button.ContextMenu>
</Button>

可以通过 ItemsSource 动态生成菜单项。

<Button Content="右键点击我">
    <Button.ContextMenu>
        <ContextMenu ItemsSource="{Binding MenuItems}">
            <ContextMenu.ItemTemplate>
                <DataTemplate>
                    <MenuItem Header="{Binding Name}" Command="{Binding Command}" />
                </DataTemplate>
            </ContextMenu.ItemTemplate>
        </ContextMenu>
    </Button.ContextMenu>
</Button>

4. Menu

Menu 是一个横向或纵向排列的菜单栏控件,通常用于应用程序的主菜单。

<Menu>
    <MenuItem Header="文件">
        <MenuItem Header="打开" />
        <MenuItem Header="保存" />
        <MenuItem Header="退出" />
    </MenuItem>
    <MenuItem Header="编辑">
        <MenuItem Header="复制" />
        <MenuItem Header="粘贴" />
    </MenuItem>
</Menu>

你可以为 MenuItem 绑定命令,以响应用户选择菜单项。

<Menu>
    <MenuItem Header="文件">
        <MenuItem Header="打开" Command="{Binding OpenCommand}" />
        <MenuItem Header="保存" Command="{Binding SaveCommand}" />
    </MenuItem>
</Menu>

MenuItem 可以显示图标,通过 Icon 属性设置。

<Menu>
    <MenuItem Header="文件">
        <MenuItem Header="打开">
            <MenuItem.Icon>
                <Image Source="open_icon.png" Width="16" Height="16" />
            </MenuItem.Icon>
        </MenuItem>
        <MenuItem Header="保存">
            <MenuItem.Icon>
                <Image Source="save_icon.png" Width="16" Height="16" />
            </MenuItem.Icon>
        </MenuItem>
    </MenuItem>
</Menu>

你可以使用 InputGestureText 设置菜单项的快捷键提示,但实际的快捷键绑定需要在 CommandBindings 中设置。

<Menu>
    <MenuItem Header="保存" InputGestureText="Ctrl+S" Command="{Binding SaveCommand}" />
</Menu>

ScrollViewer

ScrollViewer 是一个用于包含其他控件并提供水平和垂直滚动功能的容器控件。通常,控件内容超出 ScrollViewer 的边界时会自动显示滚动条。

  • HorizontalScrollBarVisibility:控制水平滚动条的可见性。
    • Disabled:禁用水平滚动条。
    • Auto:根据需要自动显示滚动条(当内容超出时显示)。
    • Hidden:滚动条不可见,但内容依然可以水平滚动。
    • Visible:始终显示滚动条。
  • VerticalScrollBarVisibility:控制垂直滚动条的可见性,使用方式和 HorizontalScrollBarVisibility 相同。
  • CanContentScroll:如果为 True,表示滚动时基于项(如行、列)进行,而不是基于像素。
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
    <StackPanel>
        <TextBlock Text="Item 1" Height="100"/>
        <TextBlock Text="Item 2" Height="100"/>
        <TextBlock Text="Item 3" Height="100"/>
        <TextBlock Text="Item 4" Height="100"/>
        <!-- 内容超出 ScrollViewer 可视区域时,会显示滚动条 -->
    </StackPanel>
</ScrollViewer>

StackPanel 并不自带滚动功能。当 StackPanel 中的内容超出它的可视区域时,控件本身不会自动提供滚动条,通常需要将 StackPanel 放入 ScrollViewer 中。

ListView 自带滚动条功能,当它的内容超出可视区域时,会自动显示垂直和/或水平滚动条。

  • ListView 继承自 ItemsControl,它包含 ScrollViewer 控件,可以通过设置 ScrollViewer.HorizontalScrollBarVisibilityScrollViewer.VerticalScrollBarVisibility 来控制滚动条。
    <ListView ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto">
        <ListViewItem>Item 1</ListViewItem>
        <ListViewItem>Item 2</ListViewItem>
        <ListViewItem>Item 3</ListViewItem>
        <ListViewItem>Item 4</ListViewItem>
        <ListViewItem>Item 5</ListViewItem>
    </ListView>
    

你以自定义滚动条的外观,修改滚动条的样式。
例如,通过 ControlTemplate 可以修改滚动条的视觉效果,或者通过 Style 设置其宽度、高度等。

<ScrollViewer VerticalScrollBarVisibility="Auto">
    <ScrollViewer.Resources>
        <Style TargetType="ScrollBar">
            <Setter Property="Width" Value="12"/>
            <Setter Property="Background" Value="LightGray"/>
        </Style>
    </ScrollViewer.Resources>
    <StackPanel>
        <TextBlock Text="Custom ScrollBar 1" Height="100"/>
        <TextBlock Text="Custom ScrollBar 2" Height="100"/>
        <TextBlock Text="Custom ScrollBar 3" Height="100"/>
    </StackPanel>
</ScrollViewer>

CanContentScroll 和虚拟化

  • CanContentScroll:在 ListViewDataGrid 中,设置 CanContentScrollTrue 时,滚动将基于项进行,而非像素。这意味着滚动时是整行或整列的跳跃。
  • 虚拟化:虚拟化是为了提高性能的一种机制,通常在 ListViewDataGrid 中使用。当启用虚拟化时,控件只会渲染可见的项,未显示的项不会创建,滚动时动态加载。

可以通过 ScrollViewerScrollToVerticalOffsetScrollToHorizontalOffset 方法手动控制滚动条的位置。

scrollViewer.ScrollToVerticalOffset(100); // 垂直滚动到100的位置
scrollViewer.ScrollToHorizontalOffset(50); // 水平滚动到50的位置

自定义控件

自定义控件允许你创建可重用的控件,封装特定的功能或样式。自定义控件可以分为两种主要类型:

自定义控件(Custom Control)创建新的控件类型,比如自定义一个按钮,可以具有自定义的行为和样式。通常需要定义控件的逻辑和样式,并且可以在 XAML 中使用。
自定义用户控件(Custom UserControl):创建控件的组合,通常用于封装常用的界面元素或布局。用户控件是对现有控件的组合和封装,可以在 XAML 中直接使用。

自定义控件
using System.Windows;
using System.Windows.Controls;

namespace MyCustomControls
{
    public class MyCustomButton : Control
    {
        static MyCustomButton()
        {
            // 设置默认样式
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomButton), 
                 new FrameworkPropertyMetadata(typeof(MyCustomButton)));
        }

        // 自定义属性
        public static readonly DependencyProperty CustomProperty = DependencyProperty.Register(
            "Custom", typeof(string), typeof(MyCustomButton), new PropertyMetadata("Default"));

        public string Custom
        {
            get { return (string)GetValue(CustomProperty); }
            set { SetValue(CustomProperty, value); }
        }
    }
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:MyCustomControls">
    <Style TargetType="{x:Type local:MyCustomButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyCustomButton}">
                    <Border Background="LightBlue" BorderBrush="DarkBlue" BorderThickness="1">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Style>
    </ResourceDictionary>

在 XAML 文件中引入自定义控件的命名空间,然后像使用其他控件一样使用自定义控件。

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyCustomControls"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:MyCustomButton Custom="Hello World" Width="200" Height="50"/>
    </Grid>
</Window>
自定义用户控件

用户控件通常继承自 UserControl 类,可以包含 XAML 和后台代码。

<!-- MyUserControl.xaml -->
<UserControl x:Class="MyUserControls.MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <TextBlock Text="{Binding CustomText, RelativeSource={RelativeSource AncestorType=UserControl}}" />
    </Grid>
</UserControl>
// MyUserControl.xaml.cs
using System.Windows.Controls;

namespace MyUserControls
{
    public partial class MyUserControl : UserControl
    {
        public MyUserControl()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty CustomTextProperty =
            DependencyProperty.Register("CustomText", typeof(string), typeof(MyUserControl), new PropertyMetadata("Default"));

        public string CustomText
        {
            get { return (string)GetValue(CustomTextProperty); }
            set { SetValue(CustomTextProperty, value); }
        }
    }
}

在 XAML 文件中引入用户控件的命名空间,然后像使用其他控件一样使用自定义用户控件。

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyUserControls"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:MyUserControl CustomText="Hello User Control" Width="200" Height="100"/>
    </Grid>
</Window>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容