wpf的低调⾃定义属性⾯板PropertyGrid
当没有轮⼦的时候,就⾃⼰制作轮⼦。
前⾔
项⽬上的需求,我想需要⽤到这样⼀个跟vs属性编辑⼀样的东西,专业叫法,属性⾯板
怎么弄呢?
百度⼀下,wpf的PropertyGrid,如下:
上问wpf跟vs属性编辑类似的东西有⼈弄过吗
开始
为了要体现我的卑微,这⾥要做⼀下说明:
刚接触wpf不久(不对,以前也看过这⽅⾯的东西,就是没实际项⽬),刚好两个⽉前,项⽬要⽤wpf弄,然后就开⼲。
很多东西都是边研究边做的。
上⾯那段是我⼀年前写的,本来当时做出来之后就想写个博⽂,没完成,现在把它完成了。
这⾥先介绍⼀个wpf控件库HandyControl,我⼀年前⽤的时候控件还没那么多,现在也有PropertyGrid,具体表现如下:
是不是很酷炫,我最近更新才看到的,害,可惜了。
本来想替换掉我写的,但很⿇烦:1.功能 2.现有项⽬布局。
我写的是这样的:
跟HandyControl样式⽅⾯差别很⼤,那是因为我把样式Style = null,使⽤的全部原⽣的样式,所以如果你想酷炫,完全可以⾃⼰改,这⾥我只讲这个控件的实现思路。
怎么来?慢慢来。
1.分析这个控件功能:显⽰对象属性,并对其进⾏分类和编辑
2.分析控件显⽰布局,可以参考vs的属性⾯板
肯定有⼈头⼤,vs属性⾯板那么多功能,哇,烦躁。
有⼈欲求不得,所以烦躁。简单的讲就是想太多
把⼀个东西,⼀件事分成n件事情来做,然后把每步做好,这件事就做好了。
如果很乱,你就写下来。vs属性⾯板很复杂,那简化⼀下,就展⽰⼀个属性,做成下⾯这样:
以上的分析,我们就知道了控件的两个重要的东西,逻辑和布局。
第⼀步:创建测试类
public class Test:ViewModelBase
{
private string _Name;
///<summary>
/// Name 属性更改通知
///</summary>
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
RaisePropertyChanged(() => Name);
}
}
}
ViewModelBase只是为了使⽤RaisePropertyChanged触发属性变化,引⽤⾃GalaSoft.MvvmLight
既然是编辑对象的属性,那肯定少不了Attribute,所以需要写⼀个描述对象属性的Attribute,如下:
///<summary>
///可对字段应⽤的 PropertyGrid 特征
///</summary>
[AttributeUsage(AttributeTargets.All,
AllowMultiple = true, Inherited = true)]
public class LsPropertyGridAttribute : Attribute
{
/
//<summary>
///对应的板块
///</summary>
public string Plate;
///<summary>
///显⽰名称
///</summary>
public string ShowName;
public LsPropertyGridAttribute(string plate, string showName)
{
TypeName = type;
ShowName = showName;
}
}
那测试的类的name属性就可以添加上特征
public class Test:ViewModelBase
{
private string _Name;
/// <summary>
/// Name 属性更改通知
/// </summary>
[LsPropertyGrid("内容","名字")]
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
RaisePropertyChanged(() => Name);
}
}
}
接下来写PropertyGrid控件,这⾥我继承StackPanel,并且你得有个展⽰的依赖属性,⽤来赋值对象,所以它的类型是object,别问我怎么知道的,问就是掐指⼀算。
public class PropertyGrid : StackPanel
{
static PropertyGrid()
{
//设置该控件引⽤样式的键
// set the key to reference the style for this control
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid)));
}
///<summary>
///需要显⽰属性的类型
///</summary>
private object _ShowProp;
#region依赖属性
///<summary>
///显⽰该类的属性编辑
///</summary>
public object ShowProp
{
get { return (object)GetValue(ShowPropProperty); }
set { SetValue(ShowPropProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowProp. This enables animation, styling, binding,
public static readonly DependencyProperty ShowPropProperty =
DependencyProperty.Register("ShowProp", typeof(object), typeof(PropertyGrid),
new PropertyMetadata(default(object), new PropertyChangedCallback((d, e) =>
{
//属性更改事件
OnShowPropChanged(d, e);
})));
#endregion
///<summary>
/// ShowProp属性更改事件
///</summary>
///<param name="d"></param>
///<param name="e"></param>
private static void OnShowPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
上⾯的简单代码,其实已经把整个属性⾯板的代码结构给搭好了,接下来,我们慢慢完善。因为属性⾯板是⾯对所有类型的对象,所以我们需要⽤反射获取这个对象的信息
获取对象的编辑属性,然后⽣成布局,并绑定
public class PropertyGrid : StackPanel
{
static PropertyGrid()
{
//设置该控件引⽤样式的键
// set the key to reference the style for this control
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid)));
}
///<summary>
///需要显⽰属性的类型
///</summary>
private object _ShowProp;
#region依赖属性
margin属性怎么用///<summary>
///显⽰该类的属性编辑
///</summary>
public object ShowProp
{
get { return (object)GetValue(ShowPropProperty); }
set { SetValue(ShowPropProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowProp. This enables animation, styling, binding,
public static readonly DependencyProperty ShowPropProperty =
DependencyProperty.Register("ShowProp", typeof(object), typeof(PropertyGrid),
new PropertyMetadata(default(object), new PropertyChangedCallback((d, e) =>
{
//属性更改事件
OnShowPropChanged(d, e);
})));
#endregion
///<summary>
/// ShowProp属性更改事件
///</summary>
///<param name="d"></param>
///<param name="e"></param>
private static void OnShowPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var sender = d as PropertyGrid;
var newValue = e.NewValue;
if (newValue != null)
{
Type t = newValue.GetType();
sender._ShowProp = newValue;
Object[] obj = t.GetProperties();
//取属性上的⾃定义特性
foreach (PropertyInfo propInfo in obj)
{
object[] objAttrs = propInfo.GetCustomAttributes(typeof(LsPropertyGridAttribute), true);
if (objAttrs.Length > 0)
{
//获取编辑的属性特征
LsPropertyGridAttribute attr = objAttrs[0] as LsPropertyGridAttribute;
if (attr != null)
{
double positionLeft = 10;//距离左边
double positionTop = 15;//距离上
//Console.WriteLine("Type : {0}", attr.TypeName);
//板块不存在创建
TextBlock label = new TextBlock();
label.Text = attr.Plate;
label.HorizontalAlignment = HorizontalAlignment.Left;
label.Margin = new Thickness(positionLeft, positionTop, 0, 2);
label.FontSize = 16;
//超过400才有粗效果
label.FontWeight = FontWeight.FromOpenTypeWeight(600);
sender.Children.Add(label);
//板块的Grid
Grid grid = new Grid();
//grid.Width = 200;
grid.Margin = new Thickness(positionLeft, 0, 0, 2);
grid.HorizontalAlignment = HorizontalAlignment.Left;
grid.Background = Brushes.White;
//添加列
var column = new ColumnDefinition();
column.Width = new GridLength(80);
column.MinWidth = 80;
column.MaxWidth = 100;
grid.ColumnDefinitions.Add(column);
var column2 = new ColumnDefinition();
//column.Width = new GridLength(1.0, GridUnitType.Star);
column2.Width = new GridLength(1.0, GridUnitType.Auto);
column2.MinWidth = 250;
column2.MaxWidth = 250;
grid.ColumnDefinitions.Add(column2);
sender.Children.Add(grid);
var row = new RowDefinition();
row.MinHeight = 22;
grid.RowDefinitions.Add(row); //添加⾏
//左边显⽰名称
TextBlock tb = new TextBlock();
tb.Text = attr.ShowName;
tb.HorizontalAlignment = HorizontalAlignment.Left;
tb.VerticalAlignment = VerticalAlignment.Center;
tb.Margin = new Thickness(0, 0, 0, 0);
//通过代码修改控件的Grid.Row属性
Grid.SetRow(tb, grid.RowDefinitions.Count - 1);
Grid.SetColumn(tb, 0);
grid.Children.Add(tb);
//根据执⾏属性的名称绑定到控件
Binding binding = new Binding(propInfo.Name);
binding.Source = newValue;
binding.Mode = BindingMode.TwoWay;
var control = new TextBox();
control.Style = null;
//回车触发绑定
control.PreviewKeyDown += Control_PreviewKeyDown;
//binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
control.SetBinding(System.Windows.Controls.TextBox.TextProperty, binding);
control.VerticalAlignment = VerticalAlignment.Center;
//通过代码修改控件的Grid.Row属性
Grid.SetRow(control, grid.RowDefinitions.Count - 1);
Grid.SetColumn(control, 1);
grid.Children.Add(control);
}
}
}
}
}
/
//<summary>
///回车触发数据改变
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
private static void Control_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
var temp = sender as WControls.TextBox;
BindingExpression binding = temp.GetBindingExpression(WControls.TextBox.TextProperty);
binding.UpdateSource();
}
}
}
⼀个最简单的属性⾯板就诞⽣了,只有⼀个属性,⽣成后,即可使⽤
窗体xaml中引⽤控件路径:xmlns:Data="clr-namespace:属性⾯板Demo.Data"
<Data:PropertyGrid x:Name="pg" HorizontalAlignment="Left" Height="100" Margin="215,107,0,0" Grid.Row="1" VerticalAlignment="Top" Width="400"/> var test = new Test();
test.Name = "wc";
pg.ShowProp = test;
如下展⽰:
其他的就⼀点⼀点的添加,同理可得了。
那下⾯我直接就上完整的代码,嗯嗯,你们都同意了(- -,你是有多懒)
控件⾥⾯有下拉框,选中,按钮(⽤来绑定触发⽅法)等,可以控制绑定控件的任何可绑定的属性,⽐如:隐藏显⽰
完整的PropertyGrid
///<summary>
///⾃定义属性显⽰控件
///</summary>
public class PropertyGrid : StackPanel
{
static PropertyGrid()
{
//设置该控件引⽤样式的键
// set the key to reference the style for this control
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(
typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid)));
}
#region字段
///<summary>
///记录⼀个板块对应的Grid
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论