ComboBoxBase.cs
上传用户:nnpulika
上传日期:2013-02-15
资源大小:597k
文件大小:19k
源码类别:

状态条

开发平台:

C#

  1. using System;
  2. using System.Collections;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Data;
  6. using System.Windows.Forms;
  7. using System.Diagnostics;
  8. using System.Security;
  9. using System.Runtime.InteropServices;
  10. using System.Text;
  11. using UtilityLibrary.General;
  12. using UtilityLibrary.Win32;
  13. namespace UtilityLibrary.WinControls
  14. {
  15. #region Helper Classes
  16. internal class EditCtrlHook : System.Windows.Forms.NativeWindow
  17. {
  18. #region Class Variables
  19. ComboBoxBase comboBox = null;
  20. bool ignoreNextPaintMessage = false;
  21. #endregion
  22. #region Constructors
  23. public EditCtrlHook(ComboBoxBase cb)
  24. {
  25. comboBox = cb;
  26. }
  27. #endregion
  28. #region Overrides
  29. protected override  void WndProc(ref Message m)
  30. {
  31. Msg currentMessage = (Msg)m.Msg;
  32. switch(m.Msg)
  33. {
  34. case ((int)Msg.WM_PAINT):
  35. if ( ignoreNextPaintMessage )
  36. {
  37. ignoreNextPaintMessage = false;
  38. return;
  39. }
  40. if ( comboBox.forceUpdate &&  comboBox.DroppedDown == false )
  41. {
  42. comboBox.forceUpdate = false;
  43. comboBox.ForceUpdate();
  44. }
  45. if ( comboBox.Enabled == false ) 
  46. {
  47. // Let the edit control do its thing first
  48. base.WndProc(ref m);
  49. // This is going to generate another paint message
  50. // ignore it
  51. ignoreNextPaintMessage = true;
  52. DrawDisableState();
  53. return;
  54. }
  55. break;
  56. case ((int)Msg.WM_MOUSEMOVE):
  57. RequestMouseLeaveMessage(m.HWnd); 
  58. if ( comboBox.highlighted == false)
  59. {
  60. using (Graphics g = Graphics.FromHwnd(comboBox.Handle))
  61. {
  62. comboBox.DrawComboBoxBorder(g, ColorUtil.VSNetBorderColor);
  63. comboBox.DrawComboBoxArrowSelected(g, false);
  64. }
  65. }
  66. break;
  67. case ((int)Msg.WM_MOUSELEAVE):
  68. if ( comboBox.highlighted == true && !comboBox.ContainsFocus)
  69. {
  70. using (Graphics g = Graphics.FromHwnd(comboBox.Handle))
  71. {
  72. comboBox.DrawComboBoxBorder(g, SystemColors.Window);
  73. comboBox.DrawComboBoxArrowNormal(g, false);
  74. }
  75. }
  76. break;
  77. default:
  78. break;
  79. }
  80. base.WndProc(ref m);
  81. }
  82. #endregion
  83. #region Implementation
  84. void RequestMouseLeaveMessage(IntPtr hWnd)
  85. {
  86. // Crea the structure needed for WindowsAPI call
  87. Win32.TRACKMOUSEEVENTS tme = new Win32.TRACKMOUSEEVENTS();
  88. // Fill in the structure
  89. tme.cbSize = 16;
  90. tme.dwFlags = Win32.TrackerEventFlags.TME_LEAVE;
  91. tme.hWnd = hWnd;
  92. tme.dwHoverTime = 0;
  93. // Request that a message gets sent when mouse leaves this window
  94. WindowsAPI.TrackMouseEvent(ref tme);
  95. }
  96. void DrawDisableState()
  97. {
  98. // we are just going to fill the edit area
  99. // with a white background, the combobox 
  100. // already does the hard part
  101. using (Graphics g = Graphics.FromHwnd(Handle))
  102. {
  103. RECT rc = new RECT();
  104. WindowsAPI.GetClientRect(Handle, ref rc);
  105. Rectangle clientSize = new Rectangle(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top);
  106. g.FillRectangle(Brushes.White, clientSize);
  107. // Draw text
  108. StringBuilder buffer = new StringBuilder(80);
  109. WindowsAPI.GetWindowText(Handle, buffer, 512);
  110. using ( Brush b = new SolidBrush(SystemColors.ControlDark) )
  111. {
  112. string text = buffer.ToString();
  113. Size textSize = TextUtil.GetTextSize(g, text, SystemInformation.MenuFont);
  114. Point pt = new Point(rc.left, rc.top + ( rc.bottom-textSize.Height )/2);
  115. g.DrawString(text, comboBox.Font, b, pt);
  116. }
  117. }
  118. }
  119. #endregion
  120. }
  121. #endregion
  122. /// <summary>
  123. /// Summary description for ComboBoxBase.
  124. /// </summary>
  125. [ToolboxItem(false)]
  126. public class ComboBoxBase : System.Windows.Forms.ComboBox
  127. {
  128. #region Class Variables
  129. private IntPtr mouseHookHandle = IntPtr.Zero;
  130. private GCHandle mouseProcHandle;
  131. private bool toolBarUse = false;
  132. private bool hooked = false;
  133. public const int ARROW_WIDTH = 12;
  134. private EditCtrlHook editHook = null;
  135. internal bool forceUpdate = false;
  136. internal bool highlighted = false;
  137. bool fireOnSelectedIndexChanged = true;
  138. #endregion
  139. #region Constructors
  140. // I wanted to make this constructor either protected
  141. // or internal so that the ComboBoxBase could not be used
  142. // as a stand alone class, however the IDE designer complains about it
  143. // if the constructors are not public
  144. public ComboBoxBase(bool toolBarUse)
  145. {
  146. // Flag to indicate that combo box will be used
  147. // in a toolbar --which means we will use some window hooks
  148. // to reset the focus of the combobox--
  149. this.toolBarUse = toolBarUse;
  150. DrawMode = DrawMode.OwnerDrawFixed;
  151. SetStyle(ControlStyles.AllPaintingInWmPaint|ControlStyles.UserPaint|ControlStyles.DoubleBuffer, true);
  152. // Use Menu font so that the combobox uses the same font as the toolbar buttons
  153. Font = SystemInformation.MenuFont;
  154. // When use in a toolbar we don't need tab stop
  155. TabStop = false;
  156.          }
  157. // I wanted to make this constructor either protected
  158. // or internal so that the ComboBoxBase could not be used
  159. // as a stand alone class, however the IDE designer complains about it
  160. // if the constructors are not public
  161. public ComboBoxBase()
  162. {
  163. DrawMode = DrawMode.OwnerDrawFixed;
  164. SetStyle(ControlStyles.AllPaintingInWmPaint
  165. |ControlStyles.UserPaint|ControlStyles.Opaque|ControlStyles.DoubleBuffer, true);
  166. }
  167. #endregion
  168. #region Overrides
  169. protected override void OnPaint(PaintEventArgs pe)
  170. {
  171. // This on paint is only going to happen for the combobox if 
  172. // the combobox has been set the style to do all painting
  173. // in the OnPaint event
  174. base.OnPaint(pe);
  175. if ( DropDownStyle == ComboBoxStyle.DropDown )
  176. {
  177. // We will handle the painting from WM_PAINT
  178. return;
  179. }
  180. if (Enabled == false)
  181. {
  182. DrawDisableState();
  183. return;
  184. }
  185. PaintComboBoxBackground(pe.Graphics, SystemColors.Window);
  186. Rectangle rc = ClientRectangle;
  187. if ( SelectedIndex == -1 ) 
  188. {
  189. if ( Items.Count > 0 )
  190. {
  191. // Select first item as the current item
  192. fireOnSelectedIndexChanged  = false;
  193. SelectedIndex = 0;
  194. }
  195. }
  196. DrawComboBoxItemEx( pe.Graphics, rc, SelectedIndex, false, true);
  197. if ( !ContainsFocus )
  198. {
  199. DrawComboBoxBorder(pe.Graphics, SystemColors.Window);
  200. DrawComboBoxArrowNormal(pe.Graphics, true);
  201. }
  202. else
  203. {
  204. DrawComboBoxBorder(pe.Graphics, ColorUtil.VSNetBorderColor);
  205. }
  206. }
  207. protected override void OnHandleCreated(EventArgs e)
  208. {
  209. base.OnHandleCreated(e);
  210. // Hook the edit control
  211. if ( DropDownStyle == ComboBoxStyle.DropDown )
  212. {
  213. IntPtr hEditControl = WindowsAPI.GetDlgItem(Handle, 0x3E9);
  214. Debug.Assert(hEditControl != IntPtr.Zero, "Fail to get ComboBox's Edit Control Handle...");
  215. if ( WindowsAPI.IsCommonCtrl6() )
  216. {
  217. // If using a manifest, mark this combo box as a Window that wants to do
  218. // its own painting
  219. WindowsAPI.DisableWindowsXPTheme(Handle);
  220. }
  221. editHook = new EditCtrlHook(this);
  222. editHook.AssignHandle(hEditControl);
  223. }
  224. }
  225. protected override void OnDrawItem(DrawItemEventArgs e)
  226. {
  227. // Draw bitmap strech to the size of the size of the combobox
  228. Graphics g = e.Graphics;
  229. Rectangle bounds = e.Bounds;
  230. bool selected = (e.State & DrawItemState.Selected) > 0;
  231. bool editSel = (e.State & DrawItemState.ComboBoxEdit ) > 0;
  232. if ( e.Index != -1 )
  233. DrawComboBoxItem(g, bounds, e.Index, selected, editSel);
  234. }
  235. protected override void WndProc(ref Message m)
  236. {
  237. bool doPainting =  false;
  238. switch(m.Msg)
  239. {
  240. case (int)Msg.WM_PAINT:
  241. doPainting = true;
  242. break;
  243. default:
  244. break;
  245. }
  246. base.WndProc(ref m);
  247. // Now let's do our own painting
  248. // we have to do it after the combox
  249. // does its own painting so that we can 
  250. // let the edit control in the combobox
  251. // take care of the text
  252. if ( doPainting )
  253. ForcePaint(ref m);
  254. }
  255. protected override void OnMouseEnter(EventArgs e)
  256. {
  257. base.OnMouseEnter(e);
  258. Graphics g = CreateGraphics();
  259. DrawComboBoxBorder(g,ColorUtil.VSNetBorderColor);
  260. DrawComboBoxArrowSelected(g, false);
  261. g.Dispose();
  262. }
  263. protected override void OnMouseLeave(EventArgs e)
  264. {
  265. base.OnMouseLeave(e);
  266. if ( !ContainsFocus )
  267. {
  268. Graphics g = CreateGraphics();
  269. DrawComboBoxBorder(g, SystemColors.Window);
  270. DrawComboBoxArrowNormal(g, false);
  271. g.Dispose();
  272. }
  273. }
  274. protected override void OnLostFocus(EventArgs e)
  275. {
  276. base.OnLostFocus(e);
  277. Graphics g = CreateGraphics();
  278. DrawComboBoxBorder(g, SystemColors.Window);
  279. DrawComboBoxArrowNormal(g, false);
  280. g.Dispose();
  281. if ( toolBarUse && hooked)
  282. {
  283. hooked = false;
  284. EndHook();
  285. }
  286. }
  287. protected override void OnGotFocus(EventArgs e)
  288. {
  289. base.OnGotFocus(e);
  290. Graphics g = CreateGraphics();
  291. DrawComboBoxBorder(g, ColorUtil.VSNetBorderColor);
  292. DrawComboBoxArrowSelected(g, false);
  293. g.Dispose();
  294. if ( toolBarUse && !hooked)
  295. {
  296. hooked = true;
  297. StartHook();
  298. }
  299. }
  300. protected override void OnSelectedIndexChanged(EventArgs e)
  301. {
  302. if ( fireOnSelectedIndexChanged )
  303. {
  304. base.OnSelectedIndexChanged(e);
  305. }
  306. fireOnSelectedIndexChanged = true;
  307. }
  308. #endregion
  309. #region Properties
  310. public bool ToolBarUse
  311. {
  312. set { toolBarUse = value; }
  313. get { return toolBarUse; }
  314. }
  315. #endregion
  316. #region Methods
  317. public void SetFontHeight(int newHeight)
  318. {
  319. FontHeight = newHeight;
  320. }
  321. #endregion
  322. #region Implementation
  323. void PaintComboBoxBackground(Graphics g, Color backColor)
  324. {
  325. Rectangle rc = ClientRectangle;
  326. rc.Inflate(-1, -1);
  327. g.FillRectangle(new SolidBrush(backColor), rc);
  328. }
  329. internal void DrawComboBoxBorder(Graphics g, Color color)
  330. {
  331.             // Keep track of what we painted last
  332. if ( color.Equals(ColorUtil.VSNetBorderColor) )
  333. {
  334. highlighted =  true;
  335. }
  336. else
  337. {
  338. highlighted = false;
  339. }
  340. Pen pen = new Pen(new SolidBrush(color), 1);
  341. g.DrawRectangle(pen, ClientRectangle.Left, ClientRectangle.Top, ClientRectangle.Width-1, ClientRectangle.Height-1);
  342. // We need to draw an extra "inner" border to erase the ugly 3D look of  the combobox
  343. using ( Pen p = new Pen(SystemBrushes.Window) )
  344. {
  345. g.DrawRectangle(p, ClientRectangle.Left+1, ClientRectangle.Top+1, 
  346. ClientRectangle.Width-SystemInformation.VerticalScrollBarWidth, ClientRectangle.Height-3);
  347. }
  348. }
  349. internal void DrawComboBoxArrowNormal(Graphics g, bool disable)
  350. {
  351. int left, top, arrowWidth, height;
  352. CalculateArrowBoxCoordinates(out left, out top, out arrowWidth, out height);
  353. // We are not going to draw the arrow background using the total
  354. // width of the arrow button in the combobox because it is too wide
  355. // and it does not look nice. However, we need to paint over the section
  356. // that correspond to the "original" arrow button dimension to avoid
  357. // clipping or painting problems
  358. Brush stripeColorBrush = new SolidBrush(ColorUtil.VSNetControlColor);
  359. if ( Enabled )
  360. {
  361. int width = SystemInformation.VerticalScrollBarWidth - ARROW_WIDTH;
  362. g.FillRectangle(SystemBrushes.Window, new Rectangle(left-width, top-2, 
  363. SystemInformation.VerticalScrollBarWidth, height+4));
  364. }
  365. if ( !disable) 
  366. {
  367. // Erase previous selected rectangle first
  368. DrawComboBoxArrowSelected(g, true);
  369. g.FillRectangle(stripeColorBrush, left, top, arrowWidth, height);
  370. }
  371. else 
  372. {
  373. // Now draw the unselected background
  374. g.FillRectangle(stripeColorBrush, left-1, top-1, arrowWidth+2, height+2);
  375. }
  376. DrawArrowGlyph(g, disable);
  377. }
  378. internal void DrawComboBoxArrowSelected(Graphics g, bool erase)
  379. {
  380. int left, top, arrowWidth, height;
  381. CalculateArrowBoxCoordinates(out left, out top, out arrowWidth, out height);
  382. // We are not going to draw the arrow background using the total
  383. // width of the arrow button in the combobox because it too wide
  384. // and it does not look nice. However, we need to paint over the section
  385. // that correspond to the "original" arrow button dimension to avoid
  386. // clipping or painting problems
  387. if ( Enabled )
  388. {
  389. int width = SystemInformation.VerticalScrollBarWidth - ARROW_WIDTH;
  390. g.FillRectangle(SystemBrushes.Window, new Rectangle(left-width, top, 
  391. SystemInformation.VerticalScrollBarWidth, height));
  392. }
  393. if ( !erase )
  394. {
  395. if (DroppedDown) 
  396. {
  397. // If showing the list portion of the combo box, draw the arrow portion background using
  398. // the highlight color with some transparency
  399. // Don't use the graphics object that we get passed because that is associated
  400. // to the edit control of the combobox and we actually want to paint on the combobox area
  401. // and not be clipped only to the edit control client area
  402. Graphics cbg = CreateGraphics();
  403. cbg.FillRectangle(new SolidBrush(ColorUtil.VSNetPressedColor), left-1, top-1, arrowWidth+2, height+2);
  404. cbg.DrawRectangle(new Pen( new SolidBrush(ColorUtil.VSNetBorderColor), 1), left-1, top-1, arrowWidth+2, height+3);
  405. // The DrawArrow won't work with the passed Graphics object since it is the one for
  406. // the edit control and we need to paint on the combobox
  407. DrawArrowGlyph(cbg, false);
  408. cbg.Dispose();
  409. forceUpdate = true;
  410. return;
  411. }
  412. else
  413. {
  414. g.FillRectangle(new SolidBrush(ColorUtil.VSNetSelectionColor), left-1, top-1, arrowWidth+2, height+2);
  415. g.DrawRectangle(new Pen( new SolidBrush(ColorUtil.VSNetBorderColor), 1), left-1, top-2, arrowWidth+2, height+3);
  416. }
  417. }
  418. else 
  419. {
  420. g.FillRectangle(SystemBrushes.Window, left-1, top-1, arrowWidth+2, height+2);
  421. }
  422. DrawArrowGlyph(g, false);
  423.          
  424. }
  425. void CalculateArrowBoxCoordinates(out int left, out int top, out int width, out int height)
  426. {
  427. Rectangle rc = ClientRectangle;
  428. width = ARROW_WIDTH;
  429. left = rc.Right - width - 2;
  430. top = rc.Top + 2;
  431. height = rc.Height - 4;
  432. }
  433. void DrawArrowGlyph(Graphics g, bool disable)
  434. {
  435.             int left, top, arrowWidth, height;
  436. CalculateArrowBoxCoordinates(out left, out top, out arrowWidth, out height);
  437. // Draw arrow glyph
  438. Point[] pts = new Point[3];
  439. pts[0] = new Point(left + arrowWidth/2 - 2, top + height/2-1);
  440. pts[1] = new Point(left + arrowWidth/2 + 3,  top + height/2-1);
  441. pts[2] = new Point(left + arrowWidth/2, (top + height/2-1) + 3);
  442. if ( disable ) 
  443. {
  444. g.FillPolygon(new SolidBrush(SystemColors.ControlDark), pts);
  445. }
  446. else 
  447. {
  448. g.FillPolygon(Brushes.Black, pts);
  449. }
  450. }
  451. void ForcePaint(ref Message m)
  452. {
  453. // Similar code to the OnPaint handler
  454. Graphics g = Graphics.FromHwnd(Handle);
  455. if (Enabled == false)
  456. {
  457. DrawDisableState();
  458. return;
  459. }
  460. Rectangle rc = ClientRectangle;
  461. if ( SelectedIndex == -1 ) 
  462. if ( Items.Count > 0 )
  463. {
  464. fireOnSelectedIndexChanged = false;
  465. SelectedIndex = 0;
  466. }
  467. DrawComboBoxItemEx( g, rc, SelectedIndex, false, true);
  468. if ( !ContainsFocus )
  469. {
  470. DrawComboBoxBorder(g, SystemColors.Window);
  471. DrawComboBoxArrowNormal(g, false);
  472. }
  473. else 
  474. {
  475.                 DrawComboBoxBorder(g, ColorUtil.VSNetBorderColor);
  476. }
  477. }
  478. internal void ForceUpdate()
  479. {
  480. using ( Graphics g = CreateGraphics())
  481. {
  482. if ( ContainsFocus )
  483. DrawComboBoxArrowSelected(g, false);
  484. }
  485. }
  486. void StartHook()
  487. {     
  488. // Mouse hook
  489. WindowsAPI.HookProc mouseHookProc = new WindowsAPI.HookProc(MouseHook);
  490. mouseProcHandle = GCHandle.Alloc(mouseHookProc);
  491. mouseHookHandle = WindowsAPI.SetWindowsHookEx(WindowsHookCodes.WH_MOUSE, 
  492. mouseHookProc, IntPtr.Zero, WindowsAPI.GetCurrentThreadId());
  493. if ( mouseHookHandle == IntPtr.Zero) 
  494. throw new SecurityException();
  495. }
  496. void EndHook()
  497. {
  498. // Unhook
  499. WindowsAPI.UnhookWindowsHookEx(mouseHookHandle);
  500. mouseProcHandle.Free();
  501. mouseHookHandle = IntPtr.Zero;
  502. }
  503. IntPtr MouseHook(int code, IntPtr wparam, IntPtr lparam) 
  504. {
  505. MOUSEHOOKSTRUCT mh = (MOUSEHOOKSTRUCT )Marshal.PtrToStructure(lparam, typeof(MOUSEHOOKSTRUCT));
  506. if ( mh.hwnd != Handle && !DroppedDown
  507. && (wparam == (IntPtr)Msg.WM_LBUTTONDOWN || wparam == (IntPtr)Msg.WM_RBUTTONDOWN) 
  508. || wparam == (IntPtr)Msg.WM_NCLBUTTONDOWN )
  509. {
  510. // Loose focus
  511. WindowsAPI.SetFocus(IntPtr.Zero);
  512. }
  513. else if ( mh.hwnd != Handle && !DroppedDown 
  514. && (wparam == (IntPtr)Msg.WM_LBUTTONUP || wparam == (IntPtr)Msg.WM_RBUTTONUP) 
  515. || wparam == (IntPtr)Msg.WM_NCLBUTTONUP )
  516. {
  517. WindowsAPI.SetFocus(IntPtr.Zero);
  518. }
  519. return WindowsAPI.CallNextHookEx(mouseHookHandle, code, wparam, lparam);
  520. }
  521. protected virtual void DrawDisableState()
  522. {
  523. Graphics g = CreateGraphics();
  524. PaintComboBoxBackground(g, SystemColors.Window);
  525. DrawComboBoxBorder(g, SystemColors.ControlDark);
  526. DrawComboBoxArrowNormal(g, true);
  527. g.Dispose();
  528. }
  529. protected virtual void DrawComboBoxItem(Graphics g, Rectangle bounds, int Index, bool selected, bool editSel)
  530. {
  531. // Draw the the combo item
  532. using ( Brush b = new SolidBrush(SystemColors.Window) )
  533. {
  534. g.FillRectangle( b, bounds.Left, bounds.Top, bounds.Width, bounds.Height);
  535. }
  536. if ( selected && !editSel)
  537. {
  538. // Draw highlight rectangle
  539. Pen p1 = new Pen(ColorUtil.VSNetBorderColor);
  540. Brush b1 = new SolidBrush(ColorUtil.VSNetSelectionColor);
  541. g.FillRectangle(b1, bounds.Left, bounds.Top, bounds.Width, bounds.Height);
  542. g.DrawRectangle(p1, bounds.Left, bounds.Top, bounds.Width-1, bounds.Height-1);
  543. p1.Dispose();
  544. b1.Dispose();
  545. }
  546. else
  547. {
  548. // Erase highlight rectangle
  549. g.FillRectangle(new SolidBrush(SystemColors.Window), bounds.Left, bounds.Top, bounds.Width, bounds.Height);
  550. if ( editSel && ContainsFocus ) 
  551. {
  552. // Draw higlighted arrow
  553. Debug.WriteLine("Drawing ComboBox Arrow...");
  554. DrawComboBoxArrowSelected(g, false);
  555. }
  556. }
  557. }
  558. protected virtual void DrawComboBoxItemEx(Graphics g, Rectangle bounds, int Index, bool selected, bool editSel)
  559. {
  560. // This function is only called form the OnPaint handler and the Graphics object passed is the one
  561. // for the combobox itself as opossed to the one for the edit control in the combobox
  562. // doing this allows us to be able to avoid clipping problems with text strings
  563. // Draw the the combo item
  564. bounds.Inflate(-3, -3);
  565. using ( Brush b = new SolidBrush(SystemColors.Window) )
  566. {
  567. g.FillRectangle( b, bounds.Left, bounds.Top, bounds.Width, bounds.Height);
  568. }
  569. if ( selected && !editSel)
  570. {
  571. // Draw highlight rectangle
  572. Pen p1 = new Pen(ColorUtil.VSNetBorderColor);
  573. Brush b1 = new SolidBrush(ColorUtil.VSNetSelectionColor);
  574. g.FillRectangle(b1, bounds.Left, bounds.Top, bounds.Width, bounds.Height);
  575. g.DrawRectangle(p1, bounds.Left, bounds.Top, bounds.Width-1, bounds.Height-1);
  576. p1.Dispose();
  577. b1.Dispose();
  578. }
  579. else
  580. {
  581. // Erase highlight rectangle
  582. g.FillRectangle(new SolidBrush(SystemColors.Window), bounds.Left, bounds.Top, bounds.Width, bounds.Height);
  583. if ( editSel && ContainsFocus ) 
  584. {
  585. // Draw higlighted arrow
  586. DrawComboBoxArrowSelected(g, false);
  587. }
  588. }
  589. }
  590. #endregion
  591. }
  592. }