目录
一、基础
1、GridSplitter分割线
2、x:static访问资源文件
3、wpf触发器
4、添加xaml资源文件
5、Convert转换器
6、多路绑定与多路转换器
二、进阶扩展
1、HierarchicalDataTemplate
2、XmlDataProvider从外部文件获取源
3、TextBox在CellTemplate中的焦点问题
4、让窗体可裁减
5_1、获取DataGrid的ScrollViewer
5_2、DataGrid绑定集合改变触发事件
5_3、ScrollViewer自动滚动到尾部
6、wpf的Behavior行为
7、导航、Page、Hyperlink
8、Cursor光标属性
9、ListView布局、滚动条
10、DataGrid文本过长换行
11、指定字体集FontFamily
12、窗体可拖动
13、虚拟化技术提升程序性能
14、查看Binding错误信息
15、DataGrid模板列中Button绑定ViewModel的Command
16、DataGrid自动生成列
17、获取icon资源
18、TextBox输入浮点数失败
WPF相关合集如下:
WPF之关于Brush及其多种派生类的定义与使用方式
WPF之关于Command命令的自定义及使用
WPF之关于常用集合控件的定义及使用
WPF之关于依赖属性与附加属性的定义及其使用方式
WPF之关于路由事件与附加事件的自定义及其使用方式
WPF之关于动画Animation的定义、使用方式及其扩展知识
WPF之关于DragDrop拖拽的定义及其多种实现方式
WPF之关于逻辑树与视觉树的遍历及其获取方式
WPF-静态(需重启)与动态(无需重启)切换多语言
了解更多控件介绍请点击:官方文档查询
将分割线加入Grid某行或某列,用户即可通过拖拽改变行或列的尺寸。
垂直拖拽,示例:
水平拖拽 ,示例:
注意:资源文件访问权限须更改为Public
三类型: 属性触发器、数据触发器、事件触发器
使用场景:
样式:Style.Triggers
数据模板:DataTemplate.Triggers
控件模板:ControlTemplate.Triggers
元素中定义触发器:FrameworkElement.Triggers //仅支持事件触发器,否则报错
pack://application:,,, 可省略
①继承IValueConverter接口(Convert方向为Source->Target,ConvertBack反向)实现一个转换器,示例如下:
public class BoolToColorConvert : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is true) return Brushes.LightBlue;
else return Brushes.LightGreen;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
②新建一个转换器资源对象,与Binding绑定,如下:
注意:MultiBinding必须实现转换器
实现IMultiValueConverter接口的多路转换器,示例如下:
public class MultiConvert : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var high = values[0].ToString();
if (values[1] is true)
return $"{high} true";
else return $"{high} false";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
HierarchicalDataTemplate:助层级控件(TreeView、MenuItem)显示层级数据模板
注意:若结点为同类型(同节点名、同属性名),使用一个Template就行,会自动迭代;
可通过路由事件方式取出XML数据;
代码如下:
当使用GridView作为ListView的View属性时,若某一列使用TextBox作为CellTemplate,那么TextBox获取焦点时目标控件并不会把该项作为SelectedItem
解决方式:通过TextBox的GotFocus事件的事件处理器去追溯到目标控件,获取业务逻辑数据,并将其设置为选中项;
/*********访问业务逻辑数据***************/
TextBox tb=e.OriginalSource as TextBox;//获取事件发起的源头
ContentPresenter cp=tb.TemplateParent as ContentPresenter;//获取模板目标
Student stu=cp.Content as Student;//获取业务逻辑数据
this.listViewStudent.SelectedItem=stu;//设置ListView的选中项
/****************访问界面元素*************************/
ListViewItem lvi=this.listViewStudent.
itemContainerGenerator.ContainerFromItem(stu) as ListViewItem;//通过条目容器自上而下寻找
CheckBox chb=this.FindVisualChild
(lvi);//借助VisualTreeHelper封装的方法 MessageBox.Show(chb.Name);
注意:寻找DataTemplate生成的控件,若结构简单可使用DataTemplate对象的FindName方法,对于结构复杂的控件,只能借助VisualTreeHelper来实现了。
前提:窗体AllowsTransparency属性设为true
WindowStyle属性设为None
再使用Clip方法裁剪,指定一个裁剪路径
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_scroll = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(this.DataGrid, 0), 0) as ScrollViewer;
}
var itemsource = DataGrid.ItemsSource as ObservableCollection;
if (itemsource != null)
itemsource.CollectionChanged += Itemsource_CollectionChanged;
通过ScrollChanged路由事件实现该功能:
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
// 检查是否已滚动到底部
bool isAtBottom = scrollViewer.VerticalOffset >= scrollViewer.ScrollableHeight - 1;
// 如果已滚动到底部,自动滚动到底部
if (isAtBottom)
{
try
{
scrollViewer.ScrollToBottom();
}
catch
{
// ignored
}
}
}
引用System.Windows.Interactivity库
使用事件触发行为
public class MyBehavior:Behavior
导航:
1、NavigationService.GoBack(); //向下,使用前判断CanGoBack()
2、NavigationService.GoForward(); //向上,使用前判断CanGoForward()
3、NavigationService.Navigate(new Uri("Page3.xaml", UriKind.RelativeOrAbsolute));//导航
使用方法1:
BiYin
private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
Hyperlink hyperlink = (Hyperlink)sender;
Process.Start(new ProcessStartInfo(hyperlink.NavigateUri.AbsoluteUri));
//Process.Start(new ProcessStartInfo("https://www.csdn.net/"));
}
使用2:结合Page使用
this.fram.Navigate(new Uri("Page2.xaml", UriKind.RelativeOrAbsolute));
FrameworkElement的属性,可设置控件区域光标的不同状态
1、ItemsPanel:可设置数据项布局水平或垂直
2、ItemContainerStyle :设置每个项的样式,
BasedOn:继承指定对象
3、Template:可通过模板给无ScrollViewer功能的容器添加ScrollViewer
1、ScrollViewer控件:封装了水平、垂直ScrollBar和一个内容容器
2、ItemsPresenter:itemsControl不负责呈现控件,通过子元素ItemsPresenter负责,放在模板内部,该子元素会检测其父元素是否为集合控件,若是则添至视觉树中
————————————————更详细点击:
转到示例
通过Column的ElementStyle设置TextBlock的属性如下(注意:Width属性必须设置后才有效)
,该方法会多显示一行,如果太长仍不能显示全部:
①添加字体资源文件;
②双击字体文件可查看字体名称;
③指定字体集;
private void Top_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed) DragMove();
}
Data Virtualization 通常情况下我们说数据虚拟化是指数据源没有完全加载,仅加载当前需要显示的数据呈现给用户。
UI Virtualization 是针对数据容器渲染数据项的一个优化,WPF中VirtualizingStackPanel容器是实现了UI Virtualization的容器。
VirtualizingPanel.VirtualizationMode="Recycling"表示不循环实例化新的Item,例如Item1--Item20此时可见,拖动滚动条到Item100,再从Item100返回至Item1--Item20时,这时候Item1--Item20不会被重新实例化。默认情况下 VirtualizingPanel.VirtualizationMode="Standard"。
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsContainerVirtualizable="True"
注意:使用容器虚拟化需设置容器的高度(VirtualizingStackPanel.IsVirtualizing只显示可视部分不是代表我们看到的部分,而是容器能加载的部分。虽然我们可视部分可能只有 Height=680,但是不代表容器只有680高,那么,软件还是会将所有数据加载出来,直到达到容器最大高度。这样虚拟化功能就失效了)
private void grid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var result = e.PropertyName;
var p = (e.PropertyDescriptor as PropertyDescriptor).ComponentType.GetProperties().FirstOrDefault(x => x.Name == e.PropertyName);
if (p != null)
{
var found = p.GetCustomAttribute();
if (found != null)
{
result = found.Name;
if (found.GetAutoGenerateField() == false)
{
e.Cancel = true;
return;
}
}
else
{
//根据需求自行更改属性
e.Cancel = true;
return;
}
}
if (e.Column is DataGridComboBoxColumn)
{
var data = ((e.Column as DataGridComboBoxColumn).SelectedItemBinding as Binding);
data.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(e.Column, DataGridColumn.HeaderProperty, LanguageHelper.GetBinding(result));
}
else
{
var data = ((e.Column as DataGridBoundColumn).Binding as Binding);
data.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(e.Column, DataGridColumn.HeaderProperty, LanguageHelper.GetBinding(result));
}
}
private void CardDataGrid_AutoGeneratedColumns(object sender, EventArgs e)
{
foreach (var item in (sender as DataGrid).Columns)
{
try
{
if (item is DataGridBoundColumn)
{
var binding = ((item as DataGridBoundColumn).Binding) as Binding;
if (binding != null)
{
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
}
}
}
catch (Exception)
{
}
}
}
推荐阿里矢量图标库:https://www.iconfont.cn/
①添加需要的图标到购物车后,下载代码
②解压后,打开iconfont.js文件,提取Path部分数据
③创建该Path资源,如下:
M855.552 742.4a40.96 40.96 0 0 1 40.448 41.6c0 22.976-18.112 41.6-40.448 41.6h-282.88a40.96 40.96 0 0 1-40.448-41.6 42.24 42.24 0 0 1 11.84-29.44 39.808 39.808 0 0 1 28.544-12.16h282.944z m-99.84-99.84v282.88a40.96 40.96 0 0 1-41.6 40.448 41.024 41.024 0 0 1-41.6-40.384v-283.008a40.96 40.96 0 0 1 41.6-40.384 42.24 42.24 0 0 1 29.44 11.84c7.744 7.552 12.16 17.856 12.16 28.544zM166.4 928c-21.248 0-38.464-18.88-38.464-42.176 0-209.472 155.008-379.264 346.24-379.264 21.248 0 38.464 18.88 38.464 42.176 0 23.232-17.216 42.112-38.464 42.112-148.736 0-269.312 132.096-269.312 295.04 0 23.232-17.216 42.112-38.4 42.112zM475.52 177.92c-87.552 0-158.528 73.408-158.528 163.904s70.976 163.84 158.528 163.84c87.488 0 158.464-73.344 158.464-163.84 0-90.496-70.976-163.84-158.464-163.84z m0 409.728c-131.328 0-237.76-110.08-237.76-245.824C237.696 206.016 344.128 96 475.52 96c131.2 0 237.696 110.08 237.696 245.824 0 135.744-106.432 245.76-237.696 245.76z
或:
var icon = new Path();
icon.Data = ResourceHelper.GetResource("AddAuthrotyGeometry");
icon.SetResourceReference(Shape.FillProperty, "PrimaryTextBrush");
icon.Stretch = Stretch.Uniform;
testMenuItem.Icon = icon;
④使用资源:
问题:.net4.5之后,当TextBox绑定触发条件为UpdateSourceTrigger=PropertyChanged时,无法输入小数点,解决方案如下:
方法一:修改Xmal中的StringFormat为StringFormat={}{0},如果StringFormat限定了格式,那么该方法不可用:
方法二:在App.cs的入口函数中加一行代码“FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false”
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
base.OnStartup(e);
}
}