功能:
显示对象的属性,包括可显示属性、可编辑属性、及不可编辑属性。
1、MainWindow.xaml
<Window x:Class="FlowChart.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FlowChart"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<StackPanel DockPanel.Dock="Left" Width="300" Margin="0 0 10 0">
<StackPanel Margin="0 10 0 10">
<TextBlock Text="属性" FontWeight="Bold" Margin="0 0 0 10"/>
<local:PropertiesView x:Name="_propertiesView" Height="200"/>
</StackPanel>
</StackPanel>
<Border BorderBrush="Black" BorderThickness="1"></Border>
</DockPanel>
</Window>
2、MainWindow.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace FlowChart
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataInitialize();
}
public List<Selection> selections=new List<Selection>();
public void DataInitialize()
{
Selection selection = new Selection();
selection.Location=new Point(0,0);
selection.Size=new Size(200,200);
//selection.Name = "测试";
_propertiesView.SelectedObject= selection;
}
}
public class Selection:INotifyPropertyChanged
{
private Point _location;
public Point Location
{
get { return _location; }
set
{
_location = value;
OnPropertyChanged("Location");
}
}
private Size _size;
//[Browsable(false)]
public Size Size
{
get { return _size; }
set
{
_size = value;
OnPropertyChanged("Size");
}
}
private string _name="Test";
public string Name
{
get { return _name; }
//set { _name = value;
// OnPropertyChanged("Name");
//}
}
public override string ToString()
{
return GetType().Name;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
3、PropertiesView.xaml
<UserControl x:Class="FlowChart.PropertiesView"
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:FlowChart"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="300">
<UserControl.Resources>
<ControlTemplate x:Key="validationErrorTemplate">
<DockPanel>
<Image Source="Resources\empty.png" Height="16" Width="16" DockPanel.Dock="Right" Margin="-18 0 0 0"
ToolTip="{Binding ElementName=adorner,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
</Image>
<AdornedElementPlaceholder x:Name="adorner"/>
</DockPanel>
</ControlTemplate>
<Style x:Key="gridLineStyle" TargetType="Line">
<Setter Property="Stroke" Value="Gray" />
<Setter Property="Stretch" Value="Fill" />
<Setter Property="Grid.ZIndex" Value="1000" />
</Style>
<Style x:Key="gridHorizontalLineStyle" TargetType="Line" BasedOn="{StaticResource gridLineStyle}">
<Setter Property="X2" Value="1" />
<Setter Property="VerticalAlignment" Value="Bottom" />
<Setter Property="Grid.ColumnSpan"
Value="{Binding
Path=ColumnDefinitions.Count,
RelativeSource={RelativeSource AncestorType=Grid}}"/>
</Style>
<Style x:Key="gridVerticalLineStyle" TargetType="Line" BasedOn="{StaticResource gridLineStyle}">
<Setter Property="Y2" Value="1" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="Grid.RowSpan"
Value="{Binding
Path=RowDefinitions.Count,
RelativeSource={RelativeSource AncestorType=Grid}}"/>
</Style>
</UserControl.Resources>
<Border BorderThickness="1" BorderBrush="Black">
<DockPanel x:Name="_panel">
<Border x:Name="_label" Width="50" Height="16">
<TextBlock Text="Empty" TextAlignment="Center" Foreground="Gray"/>
</Border>
<ScrollViewer x:Name="_gridContainer" VerticalScrollBarVisibility="Auto">
<Grid x:Name="_grid">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Line Name="_vLine" Grid.Column="0" Grid.RowSpan="1000" Style="{StaticResource gridVerticalLineStyle}"/>
<GridSplitter Name="_splitter" Grid.RowSpan="1000" Margin="0,0,-2,0" Width="4"
Background="White" Opacity="0.01" Grid.ZIndex="10000"/>
</Grid>
</ScrollViewer>
</DockPanel>
</Border>
</UserControl>
4、PropertiesView.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace FlowChart
{
/// <summary>
/// PropertiesView.xaml 的交互逻辑
/// </summary>
public partial class PropertiesView : UserControl
{
public PropertiesView()
{
InitializeComponent();
DisplayProperties();
}
private object _selectedObject;
public object SelectedObject
{
get { return _selectedObject; }
set
{
if (_selectedObject != value)
{
var obj = _selectedObject as INotifyPropertyChanged;
if (obj != null)
obj.PropertyChanged -= PropertyChanged;
_selectedObject = value;
DisplayProperties();
obj = _selectedObject as INotifyPropertyChanged;
if (obj != null)
obj.PropertyChanged += PropertyChanged;
}
}
}
void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
DisplayProperties();
}
private void DisplayProperties()
{
_panel.Children.Clear();
ClearGrid();
if (SelectedObject != null)
{
int row = 0;
foreach (var prop in SelectedObject.GetType().GetProperties().OrderBy(p => p.Name))
{
var attr = prop.GetCustomAttributes(typeof(BrowsableAttribute), true);
if (attr.Length == 0 || (attr[0] as BrowsableAttribute).Browsable)
{
DisplayProperty(prop, row);
row++;
}
}
_panel.Children.Add(_gridContainer);
}
else
{
_panel.Children.Add(_label);
}
}
private void ClearGrid()
{
_grid.RowDefinitions.Clear();
for (int i = _grid.Children.Count - 1; i >= 0; i--)
{
if (_grid.Children[i] != _vLine && _grid.Children[i] != _splitter)
_grid.Children.RemoveAt(i);
}
}
private void DisplayProperty(PropertyInfo prop, int row)
{
var rowDef = new RowDefinition();
rowDef.Height = new GridLength(Math.Max(20, this.FontSize * 2));
_grid.RowDefinitions.Add(rowDef);
var tb = new TextBlock() { Text = prop.Name };
tb.Margin = new Thickness(4);
Grid.SetColumn(tb, 0);
Grid.SetRow(tb, _grid.RowDefinitions.Count - 1);
var ed = new TextBox();
ed.PreviewKeyDown += new KeyEventHandler(ed_KeyDown);
ed.Margin = new Thickness(0, 2, 14, 0);
ed.BorderThickness = new Thickness(0);
Grid.SetColumn(ed, 1);
Grid.SetRow(ed, _grid.RowDefinitions.Count - 1);
var line = new Line();
line.Style = (Style)Resources["gridHorizontalLineStyle"];
Grid.SetRow(line, row);
var binding = new Binding(prop.Name);
binding.Source = SelectedObject;
binding.ValidatesOnExceptions = true;
binding.Mode = BindingMode.OneWay;
ed.IsEnabled = false;
if (prop.CanWrite)
{
ed.IsEnabled = true;
var mi = prop.GetSetMethod();
if (mi != null && mi.IsPublic)
binding.Mode = BindingMode.TwoWay;
}
ed.SetBinding(TextBox.TextProperty, binding);
var template = (ControlTemplate)Resources["validationErrorTemplate"];
Validation.SetErrorTemplate(ed, template);
_grid.Children.Add(tb);
_grid.Children.Add(ed);
_grid.Children.Add(line);
}
void ed_KeyDown(object sender, KeyEventArgs e)
{
var ed = sender as TextBox;
if (ed != null)
{
if (e.Key == Key.Enter)
{
ed.GetBindingExpression(TextBox.TextProperty).UpdateSource();
e.Handled = true;
}
else if (e.Key == Key.Escape)
ed.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
}
}
}
}
5、运行结果