|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Drawing;
namespace System.Windows.Forms.PropertyGridInternal;
/// <summary>
/// Implements a dialog that is displayed when an unhandled exception occurs in a thread. This dialog's width
/// is defined by the summary message in the top pane. We don't restrict the dialog width in any way. Use caution
/// and check at all DPI scaling factors if adding a new message to be displayed in the top pane.
/// </summary>
internal partial class GridErrorDialog : Form
{
private TableLayoutPanel _overarchingTableLayoutPanel;
private TableLayoutPanel _buttonTableLayoutPanel;
private PictureBox _pictureBox;
private Label _messageLabel;
private DetailsButton _detailsButton;
private Button _cancelButton;
private Button _okButton;
private TableLayoutPanel _pictureLabelTableLayoutPanel;
private TextBox _detailsTextBox;
private readonly Bitmap _expandImage;
private readonly Bitmap _collapseImage;
private readonly PropertyGrid _ownerGrid;
public bool DetailsButtonExpanded { get; private set; }
[AllowNull]
public string Details
{
set => _detailsTextBox.Text = value;
}
public string Message
{
set => _messageLabel.Text = value;
}
public GridErrorDialog(PropertyGrid owner)
{
_ownerGrid = owner;
_expandImage = ScaleHelper.GetSmallIconResourceAsBitmap(typeof(ThreadExceptionDialog), "down", ScaleHelper.InitialSystemDpi);
_collapseImage = ScaleHelper.GetSmallIconResourceAsBitmap(typeof(ThreadExceptionDialog), "up", ScaleHelper.InitialSystemDpi);
InitializeComponent();
foreach (Control control in Controls)
{
if (control.SupportsUseCompatibleTextRendering)
{
control.UseCompatibleTextRenderingInternal = _ownerGrid.UseCompatibleTextRendering;
}
}
_pictureBox.Image = SystemIcons.Warning.ToBitmap();
_detailsButton.Text = $" {SR.ExDlgShowDetails}";
_detailsTextBox.AccessibleName = SR.ExDlgDetailsText;
_okButton.Text = SR.ExDlgOk;
_cancelButton.Text = SR.ExDlgCancel;
_detailsButton.Image = _expandImage;
}
/// <summary>
/// Called when the details button is clicked.
/// </summary>
private void DetailsClick(object? sender, EventArgs devent)
{
int delta = _detailsTextBox.Height + 8;
if (_detailsTextBox.Visible)
{
_detailsButton.Image = _expandImage;
DetailsButtonExpanded = false;
Height -= delta;
}
else
{
_detailsButton.Image = _collapseImage;
DetailsButtonExpanded = true;
_detailsTextBox.Width = _overarchingTableLayoutPanel.Width - _detailsTextBox.Margin.Horizontal;
Height += delta;
}
_detailsTextBox.Visible = !_detailsTextBox.Visible;
AccessibilityNotifyClients(AccessibleEvents.StateChange, -1);
AccessibilityNotifyClients(AccessibleEvents.NameChange, -1);
_detailsTextBox.TabStop = !_detailsTextBox.TabStop;
if (_detailsTextBox.Visible)
{
_detailsTextBox.Focus();
}
}
/// <summary>
/// Tells whether the current resources for this dll have been localized for a RTL language.
/// </summary>
private static bool IsRTLResources => SR.RTL != "RTL_False";
[MemberNotNull(nameof(_messageLabel))]
[MemberNotNull(nameof(_pictureBox))]
[MemberNotNull(nameof(_detailsButton))]
[MemberNotNull(nameof(_okButton))]
[MemberNotNull(nameof(_cancelButton))]
[MemberNotNull(nameof(_buttonTableLayoutPanel))]
[MemberNotNull(nameof(_pictureLabelTableLayoutPanel))]
[MemberNotNull(nameof(_overarchingTableLayoutPanel))]
[MemberNotNull(nameof(_detailsTextBox))]
private void InitializeComponent()
{
if (IsRTLResources)
{
RightToLeft = RightToLeft.Yes;
}
SuspendLayout();
_messageLabel = new()
{
AutoSize = true,
Location = new(73, 30),
Margin = new(3, 30, 3, 0),
Name = "lblMessage",
Size = new(208, 43),
TabIndex = 0
};
_pictureBox = new()
{
Location = new(3, 3),
Name = "pictureBox",
Size = new(64, 64),
SizeMode = PictureBoxSizeMode.CenterImage,
TabIndex = 5,
TabStop = false,
AutoSize = true
};
_detailsButton = new(this)
{
ImageAlign = ContentAlignment.MiddleLeft,
Location = new(3, 3),
Margin = new(12, 3, 29, 3),
Name = "detailsBtn",
Size = new(100, 23),
TabIndex = 4
};
_detailsButton.Click += DetailsClick;
_okButton = new()
{
AutoSize = true,
DialogResult = DialogResult.OK,
Location = new(131, 3),
Name = "okBtn",
Size = new(75, 23),
TabIndex = 1
};
_okButton.Click += OnButtonClick;
_cancelButton = new Button
{
AutoSize = true,
DialogResult = DialogResult.Cancel,
Location = new(212, 3),
Margin = new(0, 3, 3, 3),
Name = "cancelBtn",
Size = new(75, 23),
TabIndex = 2
};
_cancelButton.Click += OnButtonClick;
_buttonTableLayoutPanel = new TableLayoutPanel
{
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
AutoSize = true,
ColumnCount = 3,
Location = new(0, 79),
Name = "buttonTableLayoutPanel",
RowCount = 1,
Size = new(290, 29),
TabIndex = 8
};
_buttonTableLayoutPanel.SuspendLayout();
_buttonTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
_buttonTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
_buttonTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
_buttonTableLayoutPanel.Controls.Add(_cancelButton, 2, 0);
_buttonTableLayoutPanel.Controls.Add(_okButton, 1, 0);
_buttonTableLayoutPanel.Controls.Add(_detailsButton, 0, 0);
_buttonTableLayoutPanel.RowStyles.Add(new RowStyle());
_pictureLabelTableLayoutPanel = new()
{
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowOnly,
ColumnCount = 2,
Dock = DockStyle.Fill,
Location = new(3, 3),
Name = "pictureLabelTableLayoutPanel",
RowCount = 1,
Size = new(284, 73),
TabIndex = 4
};
_pictureLabelTableLayoutPanel.SuspendLayout();
_pictureLabelTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
_pictureLabelTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
_pictureLabelTableLayoutPanel.Controls.Add(_messageLabel, 1, 0);
_pictureLabelTableLayoutPanel.Controls.Add(_pictureBox, 0, 0);
_pictureLabelTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
_overarchingTableLayoutPanel = new()
{
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink,
Location = new(1, 0),
Size = new(290, 108),
MinimumSize = new(279, 50),
Name = "overarchingTableLayoutPanel",
ColumnCount = 1,
RowCount = 2,
TabIndex = 6
};
_overarchingTableLayoutPanel.SuspendLayout();
_overarchingTableLayoutPanel.ColumnStyles.Add(new ColumnStyle());
_overarchingTableLayoutPanel.Controls.Add(_buttonTableLayoutPanel, 0, 1);
_overarchingTableLayoutPanel.Controls.Add(_pictureLabelTableLayoutPanel, 0, 0);
_overarchingTableLayoutPanel.RowStyles.Add(new RowStyle());
_overarchingTableLayoutPanel.RowStyles.Add(new RowStyle());
_overarchingTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F));
_overarchingTableLayoutPanel.SetColumnSpan(_buttonTableLayoutPanel, 2);
_detailsTextBox = new()
{
Location = new(4, 114),
Multiline = true,
Name = "details",
ReadOnly = true,
ScrollBars = ScrollBars.Vertical,
Size = new(273, 100),
TabIndex = 3,
TabStop = false,
Visible = false
};
((System.ComponentModel.ISupportInitialize)(_pictureBox)).BeginInit();
AcceptButton = _okButton;
AutoSize = true;
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
AutoSizeMode = AutoSizeMode.GrowAndShrink;
CancelButton = _cancelButton;
ClientSize = new(299, 113);
Controls.Add(_detailsTextBox);
Controls.Add(_overarchingTableLayoutPanel);
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
Name = "Form1";
ShowInTaskbar = false;
StartPosition = FormStartPosition.CenterScreen;
_overarchingTableLayoutPanel.ResumeLayout(performLayout: false);
_overarchingTableLayoutPanel.PerformLayout();
_buttonTableLayoutPanel.ResumeLayout(performLayout: false);
_buttonTableLayoutPanel.PerformLayout();
_pictureLabelTableLayoutPanel.ResumeLayout(performLayout: false);
((System.ComponentModel.ISupportInitialize)_pictureBox).EndInit();
ResumeLayout(performLayout: false);
PerformLayout();
}
private void OnButtonClick(object? s, EventArgs e)
{
DialogResult = ((Button)s!).DialogResult;
Close();
}
protected override void OnVisibleChanged(EventArgs e)
{
if (Visible)
{
// Make sure the details button is sized properly.
using (Graphics g = CreateGraphics())
{
SizeF sizef = PropertyGrid.MeasureTextHelper.MeasureText(_ownerGrid, g, _detailsButton.Text, _detailsButton.Font);
int detailsWidth = (int)Math.Ceiling(sizef.Width);
detailsWidth += _detailsButton.Image!.Width;
_detailsButton.Width = (int)Math.Ceiling(detailsWidth * (_ownerGrid.UseCompatibleTextRendering ? 1.15f : 1.4f));
_detailsButton.Height = _okButton.Height;
}
// Update the location of the TextBox details.
int x = _detailsTextBox.Location.X;
int y = _detailsButton.Location.Y + _detailsButton.Height + _detailsButton.Margin.Bottom;
// Location is relative to its parent.
Control? parent = _detailsButton.Parent;
while (parent is not null and not Form)
{
y += parent.Location.Y;
parent = parent.Parent;
}
_detailsTextBox.Location = new Point(x, y);
if (_detailsTextBox.Visible)
{
DetailsClick(_detailsTextBox, EventArgs.Empty);
}
}
_okButton.Focus();
}
}
|