PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/Snoop/PropertyGrid2.xaml.cs

http://snoopwpf.codeplex.com
C# | 431 lines | 352 code | 63 blank | 16 comment | 59 complexity | 133dcd21838e06a6844c16772817ac2b MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. // (c) Copyright Cory Plotts.
  2. // This source is subject to the Microsoft Public License (Ms-PL).
  3. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
  4. // All other rights reserved.
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Collections.ObjectModel;
  8. using System.ComponentModel;
  9. using System.Diagnostics;
  10. using System.Windows;
  11. using System.Windows.Controls;
  12. using System.Windows.Input;
  13. using System.Windows.Threading;
  14. using Snoop.Infrastructure;
  15. namespace Snoop
  16. {
  17. public partial class PropertyGrid2 : INotifyPropertyChanged
  18. {
  19. public static readonly RoutedCommand ShowBindingErrorsCommand = new RoutedCommand();
  20. public static readonly RoutedCommand ClearCommand = new RoutedCommand();
  21. public static readonly RoutedCommand SortCommand = new RoutedCommand();
  22. public PropertyGrid2()
  23. {
  24. this.processIncrementalCall = new DelayedCall(this.ProcessIncrementalPropertyAdd, DispatcherPriority.Background);
  25. this.filterCall = new DelayedCall(this.ProcessFilter, DispatcherPriority.Background);
  26. this.InitializeComponent();
  27. this.Loaded += this.HandleLoaded;
  28. this.Unloaded += this.HandleUnloaded;
  29. this.CommandBindings.Add(new CommandBinding(PropertyGrid2.ShowBindingErrorsCommand, this.HandleShowBindingErrors, this.CanShowBindingErrors));
  30. this.CommandBindings.Add(new CommandBinding(PropertyGrid2.ClearCommand, this.HandleClear, this.CanClear));
  31. this.CommandBindings.Add(new CommandBinding(PropertyGrid2.SortCommand, this.HandleSort));
  32. filterTimer = new DispatcherTimer();
  33. filterTimer.Interval = TimeSpan.FromSeconds(0.3);
  34. filterTimer.Tick += (s, e) =>
  35. {
  36. this.filterCall.Enqueue();
  37. filterTimer.Stop();
  38. };
  39. }
  40. public bool NameValueOnly
  41. {
  42. get
  43. {
  44. return _nameValueOnly;
  45. }
  46. set
  47. {
  48. _nameValueOnly = value;
  49. GridView gridView = this.ListView != null && this.ListView.View != null ? this.ListView.View as GridView : null;
  50. if (_nameValueOnly && gridView != null && gridView.Columns.Count != 2)
  51. {
  52. gridView.Columns.RemoveAt(0);
  53. while (gridView.Columns.Count > 2)
  54. {
  55. gridView.Columns.RemoveAt(2);
  56. }
  57. }
  58. }
  59. }
  60. private bool _nameValueOnly = false;
  61. public ObservableCollection<PropertyInformation> Properties
  62. {
  63. get { return this.properties; }
  64. }
  65. private ObservableCollection<PropertyInformation> properties = new ObservableCollection<PropertyInformation>();
  66. private ObservableCollection<PropertyInformation> allProperties = new ObservableCollection<PropertyInformation>();
  67. public object Target
  68. {
  69. get { return this.GetValue(PropertyGrid2.TargetProperty); }
  70. set { this.SetValue(PropertyGrid2.TargetProperty, value); }
  71. }
  72. public static readonly DependencyProperty TargetProperty =
  73. DependencyProperty.Register
  74. (
  75. "Target",
  76. typeof(object),
  77. typeof(PropertyGrid2),
  78. new PropertyMetadata(new PropertyChangedCallback(PropertyGrid2.HandleTargetChanged))
  79. );
  80. private static void HandleTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  81. {
  82. PropertyGrid2 propertyGrid = (PropertyGrid2)d;
  83. propertyGrid.ChangeTarget(e.NewValue);
  84. }
  85. private void ChangeTarget(object newTarget)
  86. {
  87. if (this.target != newTarget)
  88. {
  89. this.target = newTarget;
  90. foreach (PropertyInformation property in this.properties)
  91. {
  92. property.Teardown();
  93. }
  94. this.RefreshPropertyGrid();
  95. this.OnPropertyChanged("Type");
  96. }
  97. }
  98. public PropertyInformation Selection
  99. {
  100. get { return this.selection; }
  101. set
  102. {
  103. this.selection = value;
  104. this.OnPropertyChanged("Selection");
  105. }
  106. }
  107. private PropertyInformation selection;
  108. public Type Type
  109. {
  110. get
  111. {
  112. if (this.target != null)
  113. return this.target.GetType();
  114. return null;
  115. }
  116. }
  117. protected override void OnFilterChanged()
  118. {
  119. base.OnFilterChanged();
  120. filterTimer.Stop();
  121. filterTimer.Start();
  122. }
  123. /// <summary>
  124. /// Delayed loading of the property inspector to avoid creating the entire list of property
  125. /// editors immediately after selection. Keeps that app running smooth.
  126. /// </summary>
  127. /// <param name="performInitialization"></param>
  128. /// <returns></returns>
  129. private void ProcessIncrementalPropertyAdd()
  130. {
  131. int numberToAdd = 10;
  132. if (this.propertiesToAdd == null)
  133. {
  134. this.propertiesToAdd = PropertyInformation.GetProperties(this.target).GetEnumerator();
  135. numberToAdd = 0;
  136. }
  137. int i = 0;
  138. for (; i < numberToAdd && this.propertiesToAdd.MoveNext(); ++i)
  139. {
  140. // iterate over the PropertyInfo objects,
  141. // setting the property grid's filter on each object,
  142. // and adding those properties to the observable collection of propertiesToSort (this.properties)
  143. PropertyInformation property = this.propertiesToAdd.Current;
  144. property.Filter = this.Filter;
  145. if (property.IsVisible)
  146. {
  147. this.properties.Add(property);
  148. }
  149. allProperties.Add(property);
  150. // checking whether a property is visible ... actually runs the property filtering code
  151. if (property.IsVisible)
  152. property.Index = this.visiblePropertyCount++;
  153. }
  154. if (i == numberToAdd)
  155. this.processIncrementalCall.Enqueue();
  156. else
  157. this.propertiesToAdd = null;
  158. }
  159. private void HandleShowBindingErrors(object sender, ExecutedRoutedEventArgs eventArgs)
  160. {
  161. PropertyInformation propertyInformation = (PropertyInformation)eventArgs.Parameter;
  162. Window window = new Window();
  163. TextBox textbox = new TextBox();
  164. textbox.IsReadOnly = true;
  165. textbox.Text = propertyInformation.BindingError;
  166. textbox.TextWrapping = TextWrapping.Wrap;
  167. window.Content = textbox;
  168. window.Width = 400;
  169. window.Height = 300;
  170. window.Title = "Binding Errors for " + propertyInformation.DisplayName;
  171. SnoopPartsRegistry.AddSnoopVisualTreeRoot(window);
  172. window.Closing +=
  173. (s, e) =>
  174. {
  175. Window w = (Window)s;
  176. SnoopPartsRegistry.RemoveSnoopVisualTreeRoot(w);
  177. };
  178. window.Show();
  179. }
  180. private void CanShowBindingErrors(object sender, CanExecuteRoutedEventArgs e)
  181. {
  182. if (e.Parameter != null && !string.IsNullOrEmpty(((PropertyInformation)e.Parameter).BindingError))
  183. e.CanExecute = true;
  184. e.Handled = true;
  185. }
  186. private void CanClear(object sender, CanExecuteRoutedEventArgs e)
  187. {
  188. if (e.Parameter != null && ((PropertyInformation)e.Parameter).IsLocallySet)
  189. e.CanExecute = true;
  190. e.Handled = true;
  191. }
  192. private void HandleClear(object sender, ExecutedRoutedEventArgs e)
  193. {
  194. ((PropertyInformation)e.Parameter).Clear();
  195. }
  196. private ListSortDirection GetNewSortDirection(GridViewColumnHeader columnHeader)
  197. {
  198. if (!(columnHeader.Tag is ListSortDirection))
  199. return (ListSortDirection)(columnHeader.Tag = ListSortDirection.Descending);
  200. ListSortDirection direction = (ListSortDirection)columnHeader.Tag;
  201. return (ListSortDirection)(columnHeader.Tag = (ListSortDirection)(((int)direction + 1) % 2));
  202. }
  203. private void HandleSort(object sender, ExecutedRoutedEventArgs args)
  204. {
  205. GridViewColumnHeader headerClicked = (GridViewColumnHeader)args.OriginalSource;
  206. direction = GetNewSortDirection(headerClicked);
  207. switch (((TextBlock)headerClicked.Column.Header).Text)
  208. {
  209. case "Name":
  210. this.Sort(PropertyGrid2.CompareNames, direction);
  211. break;
  212. case "Value":
  213. this.Sort(PropertyGrid2.CompareValues, direction);
  214. break;
  215. case "ValueSource":
  216. this.Sort(PropertyGrid2.CompareValueSources, direction);
  217. break;
  218. }
  219. }
  220. private void ProcessFilter()
  221. {
  222. foreach (var property in this.allProperties)
  223. {
  224. if (property.IsVisible)
  225. {
  226. if (!this.properties.Contains(property))
  227. {
  228. InsertInPropertOrder(property);
  229. }
  230. }
  231. else
  232. {
  233. if (properties.Contains(property))
  234. {
  235. this.properties.Remove(property);
  236. }
  237. }
  238. }
  239. SetIndexesOfProperties();
  240. }
  241. private void InsertInPropertOrder(PropertyInformation property)
  242. {
  243. if (this.properties.Count == 0)
  244. {
  245. this.properties.Add(property);
  246. return;
  247. }
  248. if (PropertiesAreInOrder(property, this.properties[0]))
  249. {
  250. this.properties.Insert(0, property);
  251. return;
  252. }
  253. for (int i = 0; i < this.properties.Count - 1; i++)
  254. {
  255. if (PropertiesAreInOrder(this.properties[i], property) && PropertiesAreInOrder(property, this.properties[i + 1]))
  256. {
  257. this.properties.Insert(i + 1, property);
  258. return;
  259. }
  260. }
  261. this.properties.Add(property);
  262. }
  263. private bool PropertiesAreInOrder(PropertyInformation first, PropertyInformation last)
  264. {
  265. if (direction == ListSortDirection.Ascending)
  266. {
  267. return first.CompareTo(last) <= 0;
  268. }
  269. else
  270. {
  271. return last.CompareTo(first) <= 0;
  272. }
  273. }
  274. private void SetIndexesOfProperties()
  275. {
  276. for (int i = 0; i < this.properties.Count; i++)
  277. {
  278. this.properties[i].Index = i;
  279. }
  280. }
  281. private void HandleLoaded(object sender, EventArgs e)
  282. {
  283. if (this.unloaded)
  284. {
  285. this.RefreshPropertyGrid();
  286. this.unloaded = false;
  287. }
  288. }
  289. private void HandleUnloaded(object sender, EventArgs e)
  290. {
  291. foreach (PropertyInformation property in this.properties)
  292. property.Teardown();
  293. unloaded = true;
  294. }
  295. private void HandleNameClick(object sender, MouseButtonEventArgs e)
  296. {
  297. if (e.ClickCount == 2)
  298. {
  299. PropertyInformation property = (PropertyInformation)((FrameworkElement)sender).DataContext;
  300. object newTarget = null;
  301. if (Keyboard.Modifiers == ModifierKeys.Shift)
  302. newTarget = property.Binding;
  303. else if (Keyboard.Modifiers == ModifierKeys.Control)
  304. newTarget = property.BindingExpression;
  305. else if (Keyboard.Modifiers == ModifierKeys.None)
  306. newTarget = property.Value;
  307. if (newTarget != null)
  308. {
  309. PropertyInspector.DelveCommand.Execute(property, this);
  310. }
  311. }
  312. }
  313. private void Sort(Comparison<PropertyInformation> comparator, ListSortDirection direction)
  314. {
  315. Sort(comparator, direction, this.properties);
  316. Sort(comparator, direction, this.allProperties);
  317. }
  318. private void Sort(Comparison<PropertyInformation> comparator, ListSortDirection direction, ObservableCollection<PropertyInformation> propertiesToSort)
  319. {
  320. List<PropertyInformation> sorter = new List<PropertyInformation>(propertiesToSort);
  321. sorter.Sort(comparator);
  322. if (direction == ListSortDirection.Descending)
  323. sorter.Reverse();
  324. propertiesToSort.Clear();
  325. foreach (PropertyInformation property in sorter)
  326. propertiesToSort.Add(property);
  327. }
  328. private void RefreshPropertyGrid()
  329. {
  330. this.allProperties.Clear();
  331. this.properties.Clear();
  332. this.visiblePropertyCount = 0;
  333. this.propertiesToAdd = null;
  334. this.processIncrementalCall.Enqueue();
  335. }
  336. private object target;
  337. private IEnumerator<PropertyInformation> propertiesToAdd;
  338. private DelayedCall processIncrementalCall;
  339. private DelayedCall filterCall;
  340. private int visiblePropertyCount = 0;
  341. private bool unloaded = false;
  342. private ListSortDirection direction = ListSortDirection.Ascending;
  343. private DispatcherTimer filterTimer;
  344. private static int CompareNames(PropertyInformation one, PropertyInformation two)
  345. {
  346. // use the PropertyInformation CompareTo method, instead of the string.Compare method
  347. // so that collections get sorted correctly.
  348. return one.CompareTo(two);
  349. }
  350. private static int CompareValues(PropertyInformation one, PropertyInformation two)
  351. {
  352. return string.Compare(one.StringValue, two.StringValue);
  353. }
  354. private static int CompareValueSources(PropertyInformation one, PropertyInformation two)
  355. {
  356. return string.Compare(one.ValueSource.BaseValueSource.ToString(), two.ValueSource.BaseValueSource.ToString());
  357. }
  358. #region INotifyPropertyChanged Members
  359. public event PropertyChangedEventHandler PropertyChanged;
  360. protected void OnPropertyChanged(string propertyName)
  361. {
  362. Debug.Assert(this.GetType().GetProperty(propertyName) != null);
  363. if (this.PropertyChanged != null)
  364. this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  365. }
  366. #endregion
  367. }
  368. }