WPF组合控件TreeView+DataGrid之TreeView封装

(关注博主后,在“粉丝专栏”,可免费阅读此文)        

wpf的功能非常强大,很多控件都是原生的,但是要使用TreeView+DataGrid的组合,就需要我们自己去封装实现。

我们需要的效果如图所示:

这2个图都是第三方控件自带的,并且都是收费使用。

现在我们就用原生的控件进行封装一个。

本文源码效果如下,(搞了好几天,的确有难度,所以源码也收费,便宜,赚点辛苦费)

功能如图所示, 目前已经实现了一部分。

首先说明一下,实现上面的效果,有3种方法

第一种:技术的选择是TreeView(也就是本文的演示)。

第二种:技术的选择是DataGrid。

第三种:技术的选择是ListView。

本文演示的是使用TreeView的实现。

1.首先建立一个wpf程序

2. 封装TreeGrid

namespace TreeView.TreeDataGrid.Controls
{//这里有一个骚操作,就是把引用放在里面using System;using System.Collections.Specialized;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Media;public class TreeGrid : TreeView{static TreeGrid(){DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeGrid), new FrameworkPropertyMetadata(typeof(TreeGrid)));}#region ColumnMappings DependencyPropertypublic string ColumnMappings{get { return (string)GetValue(ColumnMappingsProperty); }set { SetValue(ColumnMappingsProperty, value); }}public static readonly DependencyProperty ColumnMappingsProperty =DependencyProperty.Register("ColumnMappings", typeof(string), typeof(TreeGrid),new PropertyMetadata("", new PropertyChangedCallback(TreeGrid.OnColumnMappingsPropertyChanged)));private static void OnColumnMappingsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnColumnMappingsValueChanged();}}protected void OnColumnMappingsValueChanged(){if (!string.IsNullOrEmpty(ColumnMappings)){ResetMappingColumns(ColumnMappings);}}private void ResetMappingColumns(string mapping){GridViewColumnCollection items = new GridViewColumnCollection();var columns = mapping.Split(new char[] { ';', '|' }, StringSplitOptions.RemoveEmptyEntries);foreach (var c in columns){var index = c.IndexOf(':');var title = "";var name = "";if (index > 0){title = c.Substring(0, index);name = c.Substring(index + 1);}else{title = c;name = c;}DataTemplate temp = null;var res = this.FindTreeResource<DataTemplate>(name);if (res != null && res is DataTemplate template){temp = template;}else{temp = new DataTemplate();FrameworkElementFactory element = null;if (items.Count == 0){element = new FrameworkElementFactory(typeof(TreeItemContentControl));element.SetValue(ContentControl.ContentProperty, new Binding(name));}else{element = new FrameworkElementFactory(typeof(TreeGridCell));element.SetValue(ContentControl.ContentProperty, new Binding(name));}temp.VisualTree = element;}var col = new GridViewColumn{Width = 200,Header = title,CellTemplate = temp,};items.Add(col);}Columns = items;}#endregion#region Columns DependencyPropertypublic GridViewColumnCollection Columns{get { return (GridViewColumnCollection)GetValue(ColumnsProperty); }set { SetValue(ColumnsProperty, value); }}public static readonly DependencyProperty ColumnsProperty =DependencyProperty.Register("Columns", typeof(GridViewColumnCollection), typeof(TreeGrid),new PropertyMetadata(null, new PropertyChangedCallback(TreeGrid.OnColumnsPropertyChanged)));private static void OnColumnsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnColumnsValueChanged();}}protected void OnColumnsValueChanged(){}#endregion#region RowHeight DependencyPropertypublic double RowHeight{get { return (double)GetValue(RowHeightProperty); }set { SetValue(RowHeightProperty, value); }}public static readonly DependencyProperty RowHeightProperty =DependencyProperty.Register("RowHeight", typeof(double), typeof(TreeGrid),new PropertyMetadata(30.0, new PropertyChangedCallback(TreeGrid.OnRowHeightPropertyChanged)));private static void OnRowHeightPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnRowHeightValueChanged();}}protected void OnRowHeightValueChanged(){}#endregion#region ShowCellBorder DependencyPropertypublic bool ShowCellBorder{get { return (bool)GetValue(ShowCellBorderProperty); }set { SetValue(ShowCellBorderProperty, value); }}public static readonly DependencyProperty ShowCellBorderProperty =DependencyProperty.Register("ShowCellBorder", typeof(bool), typeof(TreeGrid),new PropertyMetadata(false, new PropertyChangedCallback(TreeGrid.OnShowCellBorderPropertyChanged)));private static void OnShowCellBorderPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnShowCellBorderValueChanged();}}protected void OnShowCellBorderValueChanged(){}#endregion#region IconStroke DependencyPropertypublic Brush IconStroke{get { return (Brush)GetValue(IconStrokeProperty); }set { SetValue(IconStrokeProperty, value); }}public static readonly DependencyProperty IconStrokeProperty =DependencyProperty.Register("IconStroke", typeof(Brush), typeof(TreeGrid),new PropertyMetadata(new SolidColorBrush(Colors.LightGray), new PropertyChangedCallback(TreeGrid.OnIconStrokePropertyChanged)));private static void OnIconStrokePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnIconStrokeValueChanged();}}protected void OnIconStrokeValueChanged(){}#endregion#region CellBorderBrush DependencyPropertypublic Brush CellBorderBrush{get { return (Brush)GetValue(CellBorderBrushProperty); }set { SetValue(CellBorderBrushProperty, value); }}public static readonly DependencyProperty CellBorderBrushProperty =DependencyProperty.Register("CellBorderBrush", typeof(Brush), typeof(TreeGrid),new PropertyMetadata(new SolidColorBrush(Colors.LightGray), new PropertyChangedCallback(TreeGrid.OnCellBorderBrushPropertyChanged)));private static void OnCellBorderBrushPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e){if (obj is TreeGrid){(obj as TreeGrid).OnCellBorderBrushValueChanged();}}protected void OnCellBorderBrushValueChanged(){}#endregionprotected override DependencyObject GetContainerForItemOverride(){return new TreeGridItem();}protected override bool IsItemItsOwnContainerOverride(object item){return item is TreeGridItem;}protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e){base.OnItemsChanged(e);}}public class TreeGridItem : TreeViewItem{public event EventHandler IconStateChanged;static TreeGridItem(){DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeGridItem), new FrameworkPropertyMetadata(typeof(TreeGridItem)));}public TreeGridItem(){this.DataContextChanged += TreeGridItem_DataContextChanged;}private void TreeGridItem_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e){if (DataContext != null && DataContext is TreeItemData treeData){this.SetBinding(IsExpandedProperty, new Binding("IsExpanded") { Source = treeData, Mode = BindingMode.TwoWay });}}protected override void OnVisualParentChanged(DependencyObject oldParent){base.OnVisualParentChanged(oldParent);}protected override DependencyObject GetContainerForItemOverride(){return new TreeGridItem();}protected override bool IsItemItsOwnContainerOverride(object item){return item is TreeGridItem;}}/** https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/GridViewRowPresenter.cs,ace7d38fc902993d* GridViewRow里的每个元素,增加了一个默认的Margin,这样在设置边框的时候会比较麻烦,在运行时去掉*/public class TreeGridCell : ContentControl{static TreeGridCell(){DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeGridCell), new FrameworkPropertyMetadata(typeof(TreeGridCell)));}public TreeGridCell(){Loaded += TreeGridCell_Loaded;}private void TreeGridCell_Loaded(object sender, RoutedEventArgs e){Loaded -= TreeGridCell_Loaded;var p = VisualTreeHelper.GetParent(this);if (p != null && p is FrameworkElement f && f.Margin.Left > 0){f.Margin = new Thickness(0);}}}public static class TreeHelper{public static T FindParent<T>(this DependencyObject obj){var p = VisualTreeHelper.GetParent(obj);if (p == null){return default(T);}if (p is T tt){return tt;}return FindParent<T>(p);}public static T FindTreeResource<T>(this FrameworkElement obj, string key){if (obj == null){return default(T);}var r = obj.TryFindResource(key);if (r == null){r = Application.Current.TryFindResource(key);}if (r != null && r is T t){return t;}var p = FindParent<FrameworkElement>(obj);if (p != null){return FindTreeResource<T>(p, key);}return default(T);}}
}

3.TreeGrid.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:TreeView.TreeDataGrid.Controls"><SolidColorBrush x:Key="TreeIconStroke" Color="GreenYellow" /><Style x:Key="TreeGridItemStyle" TargetType="{x:Type local:TreeGridItem}"><Setter Property="Foreground" Value="Black"/><Setter Property="Background" Value="Transparent"/><Setter Property="IsExpanded" Value="True"/><Setter Property="BorderBrush" Value="Wheat"/><Setter Property="BorderThickness" Value="0,0,0,1"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:TreeGridItem}"><StackPanel><Border Name="Bd"Background="Transparent"BorderBrush="{TemplateBinding BorderBrush}"Padding="{TemplateBinding Padding}"><GridViewRowPresenter x:Name="PART_Header"   Content="{TemplateBinding Header}"  Columns="{Binding Path=Columns,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:TreeGrid}}" /></Border><ItemsPresenter x:Name="ItemsHost" /></StackPanel><ControlTemplate.Triggers><Trigger Property="IsExpanded" Value="false"><Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/></Trigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="HasHeader" Value="false"/><Condition Property="Width" Value="Auto"/></MultiTrigger.Conditions><Setter TargetName="PART_Header" Property="MinWidth" Value="75"/></MultiTrigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="HasHeader" Value="false"/><Condition Property="Height" Value="Auto"/></MultiTrigger.Conditions><Setter TargetName="PART_Header" Property="MinHeight" Value="19"/></MultiTrigger><MultiTrigger><!--移动变色--><MultiTrigger.Conditions><Condition Property="IsFocused" Value="False"/><Condition SourceName="Bd" Property="IsMouseOver" Value="true"/></MultiTrigger.Conditions><Setter Property="Background" Value=" red" TargetName="Bd"/></MultiTrigger><Trigger Property="IsSelected" Value="true"><!--选中的背景颜色--><Setter TargetName="Bd" Property="Background" Value="YellowGreen"/><!--<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>--><Setter Property="Foreground" Value="Red"/></Trigger><!--隔行换色--><!--<Trigger Property="AlternationIndex" Value="0" ><Setter  TargetName="Bd" Property="Background" Value="blue" /></Trigger><Trigger Property="AlternationIndex" Value="2" ><Setter  TargetName="Bd" Property="Background" Value="black" /></Trigger>--><!--<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:TreeGrid}, Path=Columns.Count  }" Value="0"><Setter TargetName="Bd" Property="Background" Value="#FFD3D3D3"/></DataTrigger><DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=local:TreeGrid}, Path=Columns.Count}" Value="2"><Setter TargetName="Bd" Property="Background" Value="#FFE6E6E6"/></DataTrigger>--><MultiTrigger><MultiTrigger.Conditions><Condition Property="IsSelected" Value="true"/><Condition Property="IsSelectionActive" Value="false"/></MultiTrigger.Conditions><Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/><Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/></MultiTrigger><Trigger Property="IsEnabled" Value="false"><Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter><Style.Triggers><!--隔行换色--><Trigger Property="AlternationIndex" Value="0" ><Setter Property="Background" Value="#e7e7e7" /></Trigger><Trigger Property="AlternationIndex" Value="1" ><Setter Property="Background" Value="#f2f2f2" /></Trigger></Style.Triggers></Style><Style TargetType="{x:Type local:TreeGridItem}" BasedOn="{StaticResource TreeGridItemStyle}"/><Style TargetType="{x:Type local:TreeGridCell}"><Setter Property="HorizontalContentAlignment" Value="Center"/><Setter Property="VerticalContentAlignment" Value="Center"/><Setter Property="BorderBrush" Value="Red"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:TreeGridCell}"><Border x:Name="CellBorder" Margin="0,0,-0.5,0"Background="{TemplateBinding Background}"BorderBrush="Red"BorderThickness="0,0,0,1"><ContentControl Content="{TemplateBinding Content}"ContentTemplate="{TemplateBinding ContentTemplate}"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"VerticalAlignment="{TemplateBinding VerticalContentAlignment}"SnapsToDevicePixels="True"/></Border><!--BorderBrush="Red"下划线颜色--><!--<ControlTemplate.Triggers><DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:TreeGrid},Path=ShowCellBorder}" Value="true"><Setter TargetName="CellBorder" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:TreeGrid},Path=CellBorderBrush}" /></DataTrigger></ControlTemplate.Triggers>--></ControlTemplate></Setter.Value></Setter></Style><Style TargetType="{x:Type local:TreeGrid}"><Setter Property="IconStroke" Value="{StaticResource TreeIconStroke}"/><Setter Property="ItemContainerStyle" Value="{StaticResource {x:Type local:TreeGridItem}}"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:TreeGrid}"><Border Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="0"><!--最大边框--><DockPanel><!--标题栏--><GridViewHeaderRowPresenter IsHitTestVisible="False" Columns="{TemplateBinding  Columns}" Height="{TemplateBinding RowHeight}"  DockPanel.Dock="Top" ></GridViewHeaderRowPresenter><ItemsPresenter  /></DockPanel></Border></ControlTemplate></Setter.Value></Setter></Style>
</ResourceDictionary>

4.代码很多,最终的源码格式

需要源码请联系我。

本文来源:

WPF组合控件TreeView+DataGrid之TreeView封装-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/237011.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

医院影像科PACS系统源码,医学影像系统,支持MPR、CPR、MIP、SSD、VR、VE三维图像处理

PACS系统是医院影像科室中应用的一种系统&#xff0c;主要用于获取、传输、存档和处理医学影像。它通过各种接口&#xff0c;如模拟、DICOM和网络&#xff0c;以数字化的方式将各种医学影像&#xff0c;如核磁共振、CT扫描、超声波等保存起来&#xff0c;并在需要时能够快速调取…

Python之json模块和pickle模块详解

json模块和pickle模块的用法 在python中&#xff0c;可以使用pickle和json两个模块对数据进行序列化操作。 其中&#xff1a; json可以用于字符串或者字典等与python数据类型之间的序列化与反序列化操作。 pickle可以用于python特有类型与python数据类型之间的序列化与反序…

回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 (多指标,多图)

回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 &#xff08;多指标&#xff0c;多图&#…

Redis基础篇-002 初识Redis

1、认识NoSQL 1.1 概念 NoSQL是一个非关系型数据库。 常见的NoSQL有&#xff1a;Redis、MongoDB 1.2 NoSQL与SQL的区别 类别SQLNoSQL数据结构结构化非结构化数据关联关联非关联查询方式SQL非SQL事务特性ACIDBASE存储方式磁盘内存扩展性垂直水平使用场景1&#xff09;数据结…

Docker安装(CentOS)+简单使用

Docker安装(CentOS) 一键卸载旧的 sudo yum remove docker* 一行代码(自动安装) 使用官方安装脚本 curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 启动 docker并查看状态 运行镜像 hello-world docker run hello-world 简单使用 使用 docker run …

docker部署个人网站项目记录(前后端分离)

背景 项目是前后端分离&#xff0c;前端有三部分&#xff0c;分别是 个人网站&#xff08;blog&#xff09;网站后台管理系统&#xff08;admin&#xff09;数据大屏&#xff08;datascreen&#xff09; 后端是基于nodejs写的后台服务 后台接口服务&#xff08;todo-nodejs…

Github项目推荐:在线rename

项目地址 GitHub - JasonGrass/rename: 在线文件批量重命名 项目简介 一个开源的在线重命名文件工具。利用了新的浏览器API获取文件句柄&#xff0c;在不上传文件的情况下对文件进行重命名。可以作为前端文件操作api学习范例。 项目截图

《每天一分钟学习C语言·五》

1、 给一个字符数组输入字符串 char arr[10]; gets[arr]; //gets函数接收回车符&#xff0c;如果直接按回车&#xff0c;gets函数会把回车符转变成空字符作为结束&#xff0c;即arr[0]’\0’;2、 文件结尾标志ctrlz表示返回NULL 自己定义的头文件里面一般有宏定义和声明&#…

Linux基础(第三部分)

目录 一、查看文件显示命令 1、cat&#xff1a;查看文件内容&#xff1b;上下合并文件 2、标准输入与标准输出&#xff1a; 3、例题&#xff1a;如何合并两个文件 4、tac 就是反方向查看 5、rev 把一行倒过来 6、hexdump 查看硬件设备 7、more和less 8、查看文件内容…

JVM知识总结,一定要记住它。

1、知识点汇总 JVM是Java运行基础,面试时一定会遇到JVM的有关问题,内容相对集中,但对只是深度要求较高 其中内存模型,类加载机制,GC是重点方面.性能调优部分更偏向应用,重点突出实践能力.编译器优化 和执行模式部分偏向于理论基础,重点掌握知识点. 需了解 内存模型各部分作用…

用最通俗的语言讲解 TCP “三次握手,四次挥手”

目录 一. 前言 二. TCP 报文的头部结构 三. 三次握手 3.1. 三次握手过程 3.2. 为什么要三次握手 四. 四次挥手 4.1. 四次挥手过程 4.2. 为什么要四次挥手 五. 大白话说 5.1. 大白话说三次握手 5.2. 大白话说四次挥手 六. 总结 一. 前言 TCP 是一种面向连接的、可靠…

AI代码翻译神器,用AI翻译代码,轻松学习不同编程语言,已开源!

体验地址&#xff0c;github地址和部署地址在文章底部 AI代码翻译器的优势 近年来&#xff0c;随着技术的快速进步&#xff0c;人工智能技术展现出了在各个领域发挥作用的巨大潜力。AI代码翻译器作为一项创新技术&#xff0c;为开发者带来了全新的可能性。这项技术运用人工智…

Flink系列之:背压下的检查点

Flink系列之&#xff1a;背压下的检查点 一、Checkpointing under backpressure二、缓冲区 Debloating三、非对齐 Checkpoints四、对齐 Checkpoint 的超时五、限制六、故障排除 一、Checkpointing under backpressure 通常情况下&#xff0c;对齐 Checkpoint 的时长主要受 Che…

【MATLAB】数据拟合第13期-基于最小二乘支持向量机的拟合

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 基于最小二乘支持向量机的拟合算法是一种数学优化技术&#xff0c;它通过最小化误差的平方和寻找数据的最佳函数匹配。这种算法在曲线拟合中应用广泛&#xff0c;包括线性拟合和非线性拟合…

[网络安全]密码字典快速生成——在线网站

目录 1.密码字典在线生成器one点击链接 ​编辑2.密码字典在线生成器two点击链接 3.密码字典在线生成器three点击链接 个人推荐生成器1&#xff0c;因为复制黏贴好用。 1.密码字典在线生成器one点击链接 2.密码字典在线生成器two点击链接 3.密码字典在线生成器three点击链接 看…

智能优化算法应用:基于饥饿游戏算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于饥饿游戏算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于饥饿游戏算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.饥饿游戏算法4.实验参数设定5.算法结果6.…

Ubuntu中基础命令使用

前言 以下指令测试来自于Ubuntu18.04 如果有说的不对的&#xff0c;欢迎指正与补充 以下指令为我学习嵌入式开发中使用过最多的指令 目录 前言 1 ls 首先我们进入到Linux操作系统中 2 touch创建一个文件 3 pwd查看当前路径 4 创建目录 5 删除文件 6 cd 目录跳转 0…

scrapy_redis概念作用和流程

scrapy_redis概念作用和流程 学习目标 了解 分布式的概念及特点了解 scarpy_redis的概念了解 scrapy_redis的作用了解 scrapy_redis的工作流程 在前面scrapy框架中我们已经能够使用框架实现爬虫爬取网站数据,如果当前网站的数据比较庞大, 我们就需要使用分布式来更快的爬取数…

JavaScript基础(数组+正则表达+字符串)

目录 1.数组 1.1创建数组 1.2字面量创建数组 1.3length函数 1.4遍历数组1 1.5遍历数组2语法糖 1.6增删改查 1push 2pop 3unshift("x",x) 4shift() 5数组的截取 slice() splice() 6concat 7reverse 2.内置对象 2.1data 2.2Math对象 2.3字符串 1c…

二维相位展开问题(讨论针对不连续相位展开算法鲁棒性)

作者&#xff1a;Munther Gdeisat博士和Francis Lilley博士 先决条件&#xff1a;为了理解本教程&#xff0c;在阅读本文档之前&#xff0c;您必须已经学习并完成“一维相位展开问题”教程。 有许多应用程序可以生成包裹的相位图像。例如合成孔径雷达&#xff08;SAR&#xf…