入门示例
- MVVM模型理解快速入门:listbox-contextmenu: How to bind a ContextMenu to a ListBox in WPF. Pure MVVM solution without any code-behind.
- wpf-tabcontrol-app: Tab control application for WPF
- WPF-TabControl: WPF TabControl with tab close button, tab new button, tab drag&drop support.
通用
- DataContext:指定数据源类型,可以在xaml里指定,可以在cs里指定。
private void Window_Loaded(object sender, RoutedEventArgs e) {
this.DataContext = invoiceModel;
}
Application
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Window
<!--告诉编译器在实际运行时,忽略设计时设置的值-->
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"
<!--指定命名空间-->
xmlns:local="clr-namespace:WpfApplication1"
<!--指定数据源会绑定ActionTabViewModal,那么后面的空间数据源在绑定的时候可以直接指定该类的字段-->
<Window.DataContext>
<local:ActionTabViewModal/>
</Window.DataContext>
<!--指定加载启动事件-->
Loaded="Window_Loaded"
// 弹出模态窗口
MyWindow dlg = new MyWindow(invoiceModel);
dlg.WindowStartupLocation = WindowStartupLocation.CenterScreen;
dlg.ShowDialog();
// 获取父窗口
MainWindow parentWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}, Mode=OneTime}"
Window.CommandBindings
<Window.CommandBindings>
<CommandBinding Command="{x:Static local:TabCommands.Close}" Executed="TabClose" />
<CommandBinding Command="{x:Static local:TabCommands.CloseAll}" Executed="TabCloseAll" />
</Window.CommandBindings>
public partial class MainWindow : Window {
private void TabClose(object sender, ExecutedRoutedEventArgs e) {
TabItem tab = (TabItem)e.OriginalSource;
tabControl.Items.Remove(tab);
}
public static class TabCommands {
public static RoutedUICommand Close = new RoutedUICommand("Close", "TabClose", typeof(TabItem));
public static RoutedUICommand CloseAll = new RoutedUICommand("Close All", "CloseAll", typeof(TabItem));
public static RoutedUICommand Tab2 = new RoutedUICommand("Tab 2", "Tab2", typeof(TabControl));
}
}
Window.InputBindings响应快捷键
<!--响应快捷键Ctrl + W-->
<Window.InputBindings>
<KeyBinding Key="W" Modifiers="Control" Command="{x:Static local:TabCommands.Close}" CommandTarget="{Binding SelectedItem, ElementName=tabControl}"/>
</Window.InputBindings>
模态窗口
var win = new MyWnd();
win.ShowDialog();
非模态窗口
var win = new MyWnd();
win.Show();
Style
<!--为所有相同类型的控件设置统一的风格-->
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Width" Value="150" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
<Style x:Key="Datepicker" TargetType="{x:Type DatePicker}" BasedOn="{StaticResource {x:Type DatePicker}}">
<Setter Property="Width" Value="150" />
<Setter Property="Height" Value="30" />
</Style>
</UserControl.Resources>
预置资源
<fa:ImageAwesome Icon="Cut" Width="70" />
Binding
IsChecked="{Binding ElementName=tabControl, Path=AllowAddNew}"
克隆控件Clone
private void TabClone(TabItem original) {
object clone;
using (var stream = new MemoryStream()) {
XamlWriter.Save(original, stream);
stream.Seek(0, SeekOrigin.Begin);
clone = XamlReader.Load(stream);
}
tabControl.Items.Add(clone);
tabControl.SelectedItem = clone;
}
/// <summary>
/// Clone an element
/// </summary>
/// <param name="elementToClone"></param>
/// <returns></returns>
public static object CloneElement(object elementToClone) {
string xaml = XamlWriter.Save(elementToClone);
return XamlReader.Load(new XmlTextReader(new StringReader(xaml)));
}
控件
控件继承树
Border
边框,可以嵌套在任意控件外
Grid
<!--RowDefinition不指定时默认为1*-->
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<!--2列3行-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
</Grid>
Button
<!--透明图片按钮-->
<Button BorderThickness="0" Background="Transparent" Width="18" Height="18" Click="Close_Button_Click">
<Image Source="close.png"/>
</Button>
Menu
?
<Style TargetType="MenuItem">
<Setter Property="CommandTarget" Value="{Binding PlacementTarget.TemplatedParent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}" />
</Style>
<!--主菜单-->
<Menu Grid.Row="0" FontSize="15">
<MenuItem Height="30" Header="Master">
<MenuItem Height="30" Header="Account" Click="Account_Click"/>
<MenuItem Height="30" Header="Account Category" Click="AccountCategory_Click"/>
</MenuItem>
<MenuItem Header="Settings">
<MenuItem Height="30" Header="Settings 1" Click="Settings_Click"/>
<MenuItem Height="30" Header="Settings 2" Click="Settings_Click"/>
<MenuItem Height="30" Header="Settings 3" Click="Settings_Click"/>
</MenuItem>
<MenuItem Header="Help"></MenuItem>
</Menu>
ContextMenu
复用菜单项
<Window.Resources>
<!-- 可复用的菜单项 -->
<CompositeCollection x:Key="TabNewMenuItems" x:Shared="false">
<MenuItem Header="Tab 1" Command="{x:Static local:TabCommands.Tab1}">
</MenuItem>
<MenuItem Header="Tab 2" Command="{x:Static local:TabCommands.Tab2}">
</MenuItem>
</CompositeCollection>
<Style TargetType="MenuItem">
<Setter Property="CommandTarget" Value="{Binding PlacementTarget.TemplatedParent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}" />
</Style>
</Window.Resources>
后面可以在多处使用:
<Button>
<Button.ContextMenu>
<ContextMenu ItemsSource="{StaticResource ResourceKey=TabNewMenuItems}">
</ContextMenu>
</Button.ContextMenu>
</Button>
<ContextMenu>
<ContextMenu.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{StaticResource ResourceKey=TabNewMenuItems}" />
<Separator />
<MenuItem Header="Close" Command="{x:Static local:TabCommands.Close}">
</MenuItem>
<MenuItem Header="Close All" Command="{x:Static local:TabCommands.CloseAll}">
</MenuItem>
<MenuItem Header="Close All Others" Command="{x:Static local:TabCommands.CloseAllOthers}">
</MenuItem>
</CompositeCollection>
</ContextMenu.ItemsSource>
</ContextMenu>
单击按钮弹出菜单
private void TabNewButton_Click(object sender, RoutedEventArgs e) {
Button btn = (Button)sender;
btn.ContextMenu.PlacementTarget = btn;
btn.ContextMenu.IsOpen = true;
}
ListView
TabControl
<!--
ItemsSource:此处省略了Path=,向上找数据源,一般是DataContext,是相对于DataContext指定的类的字段。
进入到TabControl的子节点,例如TabControl.ItemTemplate,TabControl.ContentTemplate里面的Binding便是相对于每个TabControl绑定的item的字段。
在写Close_Button_Click的响应事件时,(sender as Button).DataContext就是item
-->
<TabControl Grid.Row="1" x:Name="actionTabs" ItemsSource="{Binding Tabs}" Margin="5">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="30" Width="170">
<TextBlock Width="150" Text="{Binding Header}" FontSize="15" VerticalAlignment="Center"/>
<Button Width="18" Height="18" Click="Close_Button_Click" BorderThickness="0" Background="Transparent" HorizontalAlignment="Right">
<Image Source="close.png"/>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<UserControl Content="{Binding Content}" Padding="10"/>
</ScrollViewer>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
namespace WpfApplication1 {
/// view model for the TabControl To bind on
public class ActionTabViewModal {
// These Are the tabs that will be bound to the TabControl
public ObservableCollection<ActionTabItem> Tabs { get; set; }
}
}
// This class will be the Tab int the TabControl
public class ActionTabItem {
// This will be the text in the tab control
public string Header { get; set; }
// This will be the content of the tab control It is a UserControl whits you need to create manualy
public UserControl Content { get; set; }
}
// 在写Close_Button_Click的响应事件时,(sender as Button).DataContext就是item
private void Close_Button_Click(object sender, RoutedEventArgs e) {
ActionTabItem item = (ActionTabItem)(sender as Button).DataContext;
// This event will be thrown when on a close image clicked
vmd.Tabs.Remove(item);
if (vmd.Tabs.Count() == 0) {
actionTabs.Visibility = Visibility.Collapsed;
}
this.DataContext = vmd;
}
TabItem支持拖拽顺序
<!--TabItem支持拖拽顺序 复用该风格即可-->
</Window.Resources>
<Style TargetType="TabItem">
<Setter Property="AllowDrop" Value="True" />
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="TabItem_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewMouseMove" Handler="TabItem_PreviewMouseMove" />
<EventSetter Event="Drop" Handler="TabItem_Drop" />
</Style>
</Window.Resources>
bool _isDragging = false;
Point _dragStartingPoint;
TabItem _draggedTab;
private void TabItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
_isDragging = false;
_dragStartingPoint = e.GetPosition(null);
_draggedTab = (TabItem)sender;
}
private void TabItem_PreviewMouseMove(object sender, MouseEventArgs e) {
if (_isDragging || e.LeftButton != MouseButtonState.Pressed) return;
Point position = e.GetPosition(null);
if (Math.Abs(position.X - _dragStartingPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(position.Y - _dragStartingPoint.Y) > SystemParameters.MinimumVerticalDragDistance) {
_isDragging = true;
DragDrop.DoDragDrop(_draggedTab, _draggedTab, DragDropEffects.All);
}
}
private void TabItem_Drop(object sender, DragEventArgs e) {
TabItem targetTab = sender as TabItem;
if (targetTab == null) return;
if (!e.Data.GetDataPresent(typeof(TabItem))) return;
TabItem sourceTab = (TabItem)e.Data.GetData(typeof(TabItem));
if (targetTab == sourceTab) return;
int targetIdx = tabControl.Items.IndexOf(targetTab);
using (Dispatcher.DisableProcessing()) {
tabControl.Items.Remove(sourceTab);
tabControl.Items.Insert(targetIdx, sourceTab);
tabControl.SelectedItem = sourceTab;
}
}
<Window.Resources>
<!-- Reset default button style. -->
<Style TargetType="Button">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Foreground"
Value="White" />
<Setter Property="BorderBrush"
Value="Transparent" />
<Setter Property="BorderThickness"
Value="0" />
</Style>
<Thickness x:Key="Tab_Border_Thickness">4</Thickness>
<Thickness x:Key="Tab_Border_Thickness_Selected">4, 4, 4, 0</Thickness>
<!-- Menu items to create new tab -->
<CompositeCollection x:Key="TabNewMenuItems" x:Shared="false">
<MenuItem Header="Tab 1" Command="{x:Static local:TabCommands.Tab1}">
</MenuItem>
<MenuItem Header="Tab 2" Command="{x:Static local:TabCommands.Tab2}">
</MenuItem>
</CompositeCollection>
<Style TargetType="MenuItem">
<Setter Property="CommandTarget" Value="{Binding PlacementTarget.TemplatedParent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}" />
</Style>
<Style x:Key="TabHeaderScrollViewer"
TargetType="ScrollViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollViewer">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ScrollContentPresenter Name="PART_ScrollContentPresenter"
Grid.Row="0"
Grid.Column="0" />
<ScrollBar Name="PART_HorizontalScrollBar"
Grid.Row="0"
Grid.Column="1"
Orientation="Horizontal"
Margin="10, 0, 0, 0"
Width="30"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}">
</ScrollBar>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TabControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<DockPanel>
<!-- TabPanel -->
<ScrollViewer DockPanel.Dock="Top"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Auto"
Style="{StaticResource TabHeaderScrollViewer}">
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center">
<TabPanel IsItemsHost="True" />
<!-- New tab button -->
<Button VerticalAlignment="Center"
Click="TabNewButton_Click">
<fa:ImageAwesome Icon="Plus"
Height="7" />
<Button.ContextMenu>
<ContextMenu ItemsSource="{StaticResource ResourceKey=TabNewMenuItems}">
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</ScrollViewer>
<!-- Content -->
<ContentPresenter ContentSource="SelectedContent" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="tabItemHeaderBorder"
BorderThickness="{StaticResource Tab_Border_Thickness}" BorderBrush="DarkGray">
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" FontSize="15">
<ContentPresenter ContentSource="Header" VerticalAlignment="Center" />
</TextBlock>
<Button VerticalAlignment="Center"
Command="{x:Static local:TabCommands.Close}"
CommandTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<fa:ImageAwesome Icon="Times"
Height="8"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Button>
<StackPanel.ContextMenu>
<ContextMenu>
<ContextMenu.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{StaticResource ResourceKey=TabNewMenuItems}" />
<Separator />
<MenuItem Header="Close"
Command="{x:Static local:TabCommands.Close}">
</MenuItem>
<MenuItem Header="Close All"
Command="{x:Static local:TabCommands.CloseAll}">
</MenuItem>
<MenuItem Header="Close All Others"
Command="{x:Static local:TabCommands.CloseAllOthers}">
</MenuItem>
</CompositeCollection>
</ContextMenu.ItemsSource>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter TargetName="tabItemHeaderBorder"
Property="BorderThickness"
Value="{StaticResource ResourceKey=Tab_Border_Thickness_Selected}" />
</Trigger>
<Trigger Property="IsSelected"
Value="False">
<Setter Property="BorderBrush"
Value="LightGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="AllowDrop"
Value="True" />
<EventSetter Event="PreviewMouseLeftButtonDown"
Handler="TabItem_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewMouseMove"
Handler="TabItem_PreviewMouseMove" />
<EventSetter Event="Drop"
Handler="TabItem_Drop" />
</Style>
</Window.Resources>
添加自定义Content
<TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<UserControl Content="{Binding Content}"/>
</ScrollViewer>
</DataTemplate>
</TabControl.ContentTemplate>
声明一个自定义Content类:
public partial class ShortcutsListview : ContentControl {
public ShortcutsListview() {
InitializeComponent();
}
}
新建一个xaml布局文件:ShortcutsListview.xaml,此处略
<ContentControl x:Class="smartrun.ShortcutsListview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:smartrun"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<ListView x:Name="listView" ItemsSource="{Binding}">
</ListView>
</ContentControl>
声明一个TabItem模型:
// This class will be the Tab int the TabControl
public class ActionTabItem {
// This will be the text in the tab control
public string Header { get; set; }
// This will be the content of the tab control It is a UserControl whits you need to create manualy
public ContentControl Content { get; set; }
}
代码添加Tab项:
this.tabControl.Items.Add(new ActionTabItem { Header = "提醒", Content = new ShortcutsListview { DataContext = mReminders } });
ScrollViewer
<!--自动显示滚动条-->
<ScrollViewer VerticalScrollBarVisibility="Auto">
<UserControl Content="{Binding Content}" Padding="10"/>
</ScrollViewer>
自定义滚动条样式
<!--自定义TabHeader的ScrollViewer:滚动条和TabHeader在同一行,一行两列布局,TabHeader在左边,滚动条在右侧动态显现-->
<Style x:Key="TabHeaderScrollViewer"
TargetType="ScrollViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollViewer">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ScrollContentPresenter Name="PART_ScrollContentPresenter" Grid.Row="0" Grid.Column="0" />
<ScrollBar Name="PART_HorizontalScrollBar" Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Margin="10, 0, 0, 0" Width="30" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}">
</ScrollBar>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TabControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<DockPanel>
<!-- TabPanel -->
<ScrollViewer DockPanel.Dock="Top"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Auto"
Style="{StaticResource TabHeaderScrollViewer}">
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center">
<TabPanel IsItemsHost="True" />
<!-- New tab button -->
<Button VerticalAlignment="Center" Click="TabNewButton_Click"/>
</StackPanel>
</ScrollViewer>
<!-- Content -->
<ContentPresenter ContentSource="SelectedContent" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
UserControl
派生ContentControl,使用效果等同于ContentControl,相当于安卓里的View。
浏览器EO.WebBrowser
EO.WebBrowser,就是给.NET用的Chrome浏览器内核,可以在WinForm中嵌入Chrome浏览器,详情可以浏览官方网站。在网页上执行javascript
webView1.EvalScript("alert('hi');")
DataGrid
Expander
Separator分隔条
<!-- 竖直分隔条 -->
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Margin="0,8" BorderBrush="LightGray" BorderThickness="1"/>
ToggleButton
- 定义资源并放入资源字典里面:
<Window.Resources><sys:String x:Key="myString">Hello WPF!</sys:String></Window.Resources>
- 引用资源,这个是一个标签扩展,其实很简单,花括号里面前面是类型名,后面是一个属性=value。
<TextBox Text="{StaticResource ResourceKey=myString}" Margin="5"/>
- 后台找资源的时间是用()不是[],因为这里的FindResource是窗体的方法。
string str=this.FindResource("myString") as string;
文档信息
- 本文作者:zhupite
- 本文链接:https://zhupite.com/program/cs-ui.html
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)