<Feat>: Improve Previous Results layout and resizable result columns, increase revision to 3.9.3145.103

Cause:

- Previous Results used fixed column widths, making long procedure and purchase order values difficult to read.
- Result columns could not be resized interactively.
- Window resizing caused unnecessary UI redraws and visible lag.
- The existing result layout lacked clear column headers and visual separation.

Solution:

1. Added resizable result columns
   - Added draggable column separators.
   - Keeps column widths synchronized across all displayed result rows.
   - Added optimized resize preview to reduce UI lag.

2. Improved responsive layout
   - Enlarged the default Previous Results window.
   - Keeps Purchase Order, OK, NOK and Actions aligned to the right.
   - Uses Procedure as the flexible column when resizing the window.

3. Improved result presentation
   - Added aligned column headers.
   - Added modern column colors and styled action buttons.
   - Reduced spacing between result rows and improved separators.

4. Increased revision
   - Updated revision to 3.9.3145.103.
This commit is contained in:
2026-09-04 08:25:27 +02:00
parent 136be96e1b
commit 41408b30da
3 changed files with 1326 additions and 170 deletions
+1 -1
View File
@@ -3293,7 +3293,7 @@
<DependentUpon>DeleteFromOracleForm.cs</DependentUpon>
</Compile>
<Compile Include="UI\ResultsMI\PreviousResultCtrl.cs">
<SubType>UserControl</SubType>
<SubType>Component</SubType>
</Compile>
<Compile Include="UI\ResultsMI\PreviousResultCtrl.designer.cs">
<DependentUpon>PreviousResultCtrl.cs</DependentUpon>
+811 -36
View File
@@ -3,59 +3,148 @@
///
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using Common;
using TBF.Resources;
namespace TBF.UI.ResultsMI
{
public partial class PreviousResultCtrl : UserControl
{
readonly PreviousResultsDlg parent;
internal enum PreviousResultResizeZone
{
BatchNr,
StartTime,
EndTime,
SwVersion,
Procedure,
PurchaseOrder,
PassedCount,
FailedCount,
Count,
}
internal class PreviousResultResizeHandle : Panel
{
public PreviousResultResizeHandle()
{
SetStyle(ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
Cursor = Cursors.VSplit;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (Pen pen = new Pen(Color.FromArgb(218, 224, 232)))
{
int x = Width / 2;
e.Graphics.DrawLine(pen, x, 3, x, Height - 4);
}
}
}
public partial class PreviousResultCtrl : UserControl
{
readonly PreviousResultsDlg parent;
readonly PreviousResultsMode mode;
readonly bool sendBtnIsPrint;
public int BatchNr;
public DateTime StarTime;
public DateTime EndTime;
const int DefaultProcedureWidthIncrease = 70;
const int DefaultPurchaseOrderWidthIncrease = 16;
readonly IList<PreviousResultResizeHandle> resizeHandles =
new List<PreviousResultResizeHandle>();
readonly Color[] zoneBackColors =
{
Color.FromArgb(248, 250, 252), /// Batch number
Color.FromArgb(245, 248, 252), /// Start time
Color.FromArgb(245, 248, 252), /// End time
Color.FromArgb(247, 247, 251), /// SW version
Color.FromArgb(239, 246, 255), /// Procedure
Color.FromArgb(247, 245, 255), /// Purchase order
Color.FromArgb(240, 253, 244), /// Passed
Color.FromArgb(254, 242, 242), /// Failed
};
readonly Color[] zoneTextColors =
{
Color.FromArgb(51, 65, 85), /// Batch number
Color.FromArgb(71, 85, 105), /// Start time
Color.FromArgb(71, 85, 105), /// End time
Color.FromArgb(88, 80, 125), /// SW version
Color.FromArgb(29, 78, 216), /// Procedure
Color.FromArgb(109, 40, 217), /// Purchase order
Color.FromArgb(22, 163, 74), /// Passed
Color.FromArgb(220, 38, 38), /// Failed
};
bool resizing;
PreviousResultResizeZone resizingZone;
int resizeStartMouseX;
int resizeStartGuideScreenX;
int resizeGuideScreenX;
int resizePreviewDelta;
bool resizeGuideVisible;
int minimumRowWidth;
int availableRowWidth;
int procedureStretch;
int sentCheckBoxRightOffset;
int writeButtonRightOffset;
int sendReloadButtonRightOffset;
int showButtonRightOffset;
public int BatchNr;
public DateTime StarTime;
public DateTime EndTime;
public string Ver;
public string ProcedureName;
public string PurchaseOrder;
public int PassedCount;
public int FailedCount;
public bool Sent;
public string ProcedureName;
public string PurchaseOrder;
public int PassedCount;
public int FailedCount;
public bool Sent;
public IList<string> SerialNrs;
PreviousResultCtrl()
{
InitializeComponent();
}
PreviousResultCtrl()
{
InitializeComponent();
public PreviousResultCtrl(PreviousResultsDlg parent, int bnr, DateTime s, DateTime e,
DoubleBuffered = true;
BackColor = SystemColors.Control;
}
public PreviousResultCtrl(PreviousResultsDlg parent, int bnr, DateTime s, DateTime e,
string ver, string proc, string pOrd, int passCnt, int failCnt,
bool sent, PreviousResultsMode mode, IList<string> serialNrs = null)
: this()
{
this.parent = parent;
BatchNr = bnr;
StarTime = s;
EndTime = e;
: this()
{
this.parent = parent;
BatchNr = bnr;
StarTime = s;
EndTime = e;
Ver = ver;
ProcedureName = proc;
PurchaseOrder = pOrd;
PassedCount = passCnt;
FailedCount = failCnt;
Sent = sent;
ProcedureName = proc;
PurchaseOrder = pOrd;
PassedCount = passCnt;
FailedCount = failCnt;
Sent = sent;
this.mode = mode;
SerialNrs = serialNrs;
batchNrLabel.Text = BatchNr.ToString();
batchNrLabel.Text = BatchNr.ToString();
startTimeLabel.Text = StarTime.ToString(Constants.DateTimeFormat);
endTimeLabel.Text = EndTime.ToString(Constants.DateTimeFormat);
swVerLabel.Text = Ver;
procedureLabel.Text = ProcedureName;
purchaseOrderLabel.Text = PurchaseOrder;
passedCountLabel.Text = string.Format("{0}: {1}", Strings.Passed, PassedCount);
procedureLabel.Text = ProcedureName;
purchaseOrderLabel.Text = PurchaseOrder;
passedCountLabel.Text = string.Format("{0}: {1}", Strings.Passed, PassedCount);
failedCountLabel.Text = string.Format("{0}: {1}", Strings.Failed, FailedCount);
/// 'Sent' check box
@@ -79,10 +168,12 @@ namespace TBF.UI.ResultsMI
sendBtnIsPrint = true;
#endif
break;
case PreviousResultsMode.Reload:
sendReloadButton.Enabled = true;
sendReloadButton.Text = Strings.Restore;
break;
case PreviousResultsMode.Fix:
sendReloadButton.Enabled = true;
sendReloadButton.Text = Strings.Fix;
@@ -92,11 +183,695 @@ namespace TBF.UI.ResultsMI
/// 'Show' button
showButton.Text = Strings.Show;
writeButton.Text = "Write";
ApplyModernLook();
CreateResizeHandles();
CaptureRightAlignedControlPositions();
minimumRowWidth = Width;
ApplyBaseZoneWidthIncrease(
PreviousResultResizeZone.Procedure,
DefaultProcedureWidthIncrease);
ApplyBaseZoneWidthIncrease(
PreviousResultResizeZone.PurchaseOrder,
DefaultPurchaseOrderWidthIncrease);
UpdateRowWidth();
parent.ApplyPreviousResultLayout(this);
UpdateFieldBounds();
PositionRightAlignedControls();
}
public void UpdateSentState(bool sent)
{
this.Sent = sent;
void ApplyModernLook()
{
Font = new Font("Segoe UI", 9F, FontStyle.Regular);
StyleValueLabel(batchNrLabel,
zoneBackColors[(int)PreviousResultResizeZone.BatchNr],
zoneTextColors[(int)PreviousResultResizeZone.BatchNr],
true);
StyleValueLabel(startTimeLabel,
zoneBackColors[(int)PreviousResultResizeZone.StartTime],
zoneTextColors[(int)PreviousResultResizeZone.StartTime],
false);
StyleValueLabel(endTimeLabel,
zoneBackColors[(int)PreviousResultResizeZone.EndTime],
zoneTextColors[(int)PreviousResultResizeZone.EndTime],
false);
StyleValueLabel(swVerLabel,
zoneBackColors[(int)PreviousResultResizeZone.SwVersion],
zoneTextColors[(int)PreviousResultResizeZone.SwVersion],
false);
StyleValueLabel(procedureLabel,
zoneBackColors[(int)PreviousResultResizeZone.Procedure],
zoneTextColors[(int)PreviousResultResizeZone.Procedure],
true);
StyleValueLabel(purchaseOrderLabel,
zoneBackColors[(int)PreviousResultResizeZone.PurchaseOrder],
zoneTextColors[(int)PreviousResultResizeZone.PurchaseOrder],
true);
StyleValueLabel(passedCountLabel,
zoneBackColors[(int)PreviousResultResizeZone.PassedCount],
zoneTextColors[(int)PreviousResultResizeZone.PassedCount],
true);
StyleValueLabel(failedCountLabel,
zoneBackColors[(int)PreviousResultResizeZone.FailedCount],
zoneTextColors[(int)PreviousResultResizeZone.FailedCount],
true);
StyleButton(writeButton, Color.FromArgb(13, 148, 136));
StyleButton(sendReloadButton, GetActionButtonBackColor());
StyleButton(showButton, Color.FromArgb(37, 99, 235));
sentCheckBox.Font = new Font("Segoe UI", 9F, FontStyle.Regular);
sentCheckBox.ForeColor = Color.FromArgb(71, 85, 105);
sentCheckBox.BackColor = SystemColors.Control;
}
void StyleValueLabel(Label label, Color backColor, Color foreColor, bool bold)
{
label.AutoSize = false;
label.BackColor = backColor;
label.ForeColor = foreColor;
label.Font = new Font("Segoe UI", 9F, bold ? FontStyle.Bold : FontStyle.Regular);
label.TextAlign = ContentAlignment.MiddleLeft;
label.AutoEllipsis = true;
label.Padding = new Padding(5, 0, 4, 0);
}
void StyleButton(Button button, Color backColor)
{
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderSize = 0;
button.FlatAppearance.MouseDownBackColor = ControlPaint.Dark(backColor, 0.06f);
button.FlatAppearance.MouseOverBackColor = ControlPaint.Light(backColor, 0.06f);
button.BackColor = backColor;
button.ForeColor = Color.White;
button.Font = new Font("Segoe UI", 9F, FontStyle.Bold);
button.UseVisualStyleBackColor = false;
ApplyRoundedRegion(button, 5);
button.Resize += delegate
{
ApplyRoundedRegion(button, 5);
};
}
void ApplyRoundedRegion(Control control, int radius)
{
if (control.Width <= 0 || control.Height <= 0)
{
return;
}
int diameter = radius * 2;
Rectangle bounds =
new Rectangle(0, 0, control.Width, control.Height);
using (GraphicsPath path = new GraphicsPath())
{
path.AddArc(bounds.Left,
bounds.Top,
diameter,
diameter,
180,
90);
path.AddArc(bounds.Right - diameter - 1,
bounds.Top,
diameter,
diameter,
270,
90);
path.AddArc(bounds.Right - diameter - 1,
bounds.Bottom - diameter - 1,
diameter,
diameter,
0,
90);
path.AddArc(bounds.Left,
bounds.Bottom - diameter - 1,
diameter,
diameter,
90,
90);
path.CloseFigure();
Region oldRegion = control.Region;
control.Region = new Region(path);
if (oldRegion != null)
{
oldRegion.Dispose();
}
}
}
Color GetActionButtonBackColor()
{
if (mode == PreviousResultsMode.Reload)
{
return Color.FromArgb(99, 102, 241);
}
if (mode == PreviousResultsMode.Fix)
{
return Color.FromArgb(234, 88, 12);
}
if (sendBtnIsPrint)
{
return Color.FromArgb(71, 85, 105);
}
return Color.FromArgb(22, 163, 74);
}
void CaptureRightAlignedControlPositions()
{
sentCheckBoxRightOffset = Width - sentCheckBox.Left;
writeButtonRightOffset = Width - writeButton.Left;
sendReloadButtonRightOffset = Width - sendReloadButton.Left;
showButtonRightOffset = Width - showButton.Left;
}
void PositionRightAlignedControls()
{
sentCheckBox.Left = Width - sentCheckBoxRightOffset;
writeButton.Left = Width - writeButtonRightOffset;
sendReloadButton.Left = Width - sendReloadButtonRightOffset;
showButton.Left = Width - showButtonRightOffset;
}
void ApplyBaseZoneWidthIncrease(PreviousResultResizeZone zone, int delta)
{
int index = (int)zone;
for (int i = index; i < resizeHandles.Count; i++)
{
resizeHandles[i].Left += delta;
}
minimumRowWidth += delta;
}
void SetProcedureStretch(int value)
{
value = Math.Max(0, value);
int delta = value - procedureStretch;
if (delta == 0)
{
return;
}
for (int i = (int)PreviousResultResizeZone.Procedure;
i < resizeHandles.Count;
i++)
{
resizeHandles[i].Left += delta;
}
procedureStretch = value;
}
internal void SetAvailableWidth(int width)
{
int newWidth = Math.Max(0, width);
if (availableRowWidth == newWidth)
{
return;
}
availableRowWidth = newWidth;
UpdateRowWidth();
}
void UpdateRowWidth()
{
int newWidth =
Math.Max(
minimumRowWidth,
availableRowWidth);
int newProcedureStretch =
Math.Max(
0,
newWidth - minimumRowWidth);
SetProcedureStretch(newProcedureStretch);
if (Width != newWidth)
{
Width = newWidth;
}
PositionRightAlignedControls();
UpdateFieldBounds();
Invalidate();
}
void CreateResizeHandles()
{
AddResizeHandle(PreviousResultResizeZone.BatchNr, startTimeLabel.Left - 2);
AddResizeHandle(PreviousResultResizeZone.StartTime, endTimeLabel.Left - 2);
AddResizeHandle(PreviousResultResizeZone.EndTime, swVerLabel.Left - 2);
AddResizeHandle(PreviousResultResizeZone.SwVersion, procedureLabel.Left - 2);
AddResizeHandle(PreviousResultResizeZone.Procedure, purchaseOrderLabel.Left - 2);
AddResizeHandle(PreviousResultResizeZone.PurchaseOrder, passedCountLabel.Left - 2);
AddResizeHandle(PreviousResultResizeZone.PassedCount, failedCountLabel.Left - 2);
AddResizeHandle(PreviousResultResizeZone.FailedCount, sentCheckBox.Left - 2);
}
void AddResizeHandle(PreviousResultResizeZone zone, int left)
{
PreviousResultResizeHandle handle =
new PreviousResultResizeHandle
{
Left = left,
Top = 0,
Width = 5,
Height = Height,
Tag = zone,
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left,
};
handle.MouseDown += resizeHandle_MouseDown;
handle.MouseMove += resizeHandle_MouseMove;
handle.MouseUp += resizeHandle_MouseUp;
resizeHandles.Add(handle);
Controls.Add(handle);
handle.BringToFront();
}
int GetHandleBoundary(int index)
{
return resizeHandles[index].Left +
(resizeHandles[index].Width / 2);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (resizeHandles.Count == 0)
{
return;
}
int left = 0;
for (int i = 0;
i < resizeHandles.Count &&
i < zoneBackColors.Length;
i++)
{
int right = GetHandleBoundary(i);
if (right > left)
{
using (Brush brush =
new SolidBrush(zoneBackColors[i]))
{
e.Graphics.FillRectangle(
brush,
left,
0,
right - left,
Height);
}
}
left = right;
}
using (Pen bottomPen =
new Pen(Color.FromArgb(218, 224, 232)))
{
e.Graphics.DrawLine(
bottomPen,
0,
Height - 1,
Width,
Height - 1);
}
}
void resizeHandle_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
{
return;
}
PreviousResultResizeHandle handle =
sender as PreviousResultResizeHandle;
if (handle == null)
{
return;
}
resizing = true;
resizingZone = (PreviousResultResizeZone)handle.Tag;
resizeStartMouseX = Cursor.Position.X;
resizePreviewDelta = 0;
resizeStartGuideScreenX =
handle.PointToScreen(
new Point(handle.Width / 2, 0)).X;
resizeGuideScreenX = resizeStartGuideScreenX;
parent.DrawPreviousResultResizeGuide(
resizeGuideScreenX);
resizeGuideVisible = true;
handle.Capture = true;
}
void resizeHandle_MouseMove(object sender, MouseEventArgs e)
{
if (!resizing)
{
return;
}
int requestedDelta =
Cursor.Position.X - resizeStartMouseX;
int delta =
LimitResizeDelta(
resizingZone,
requestedDelta);
if (delta == resizePreviewDelta)
{
return;
}
if (resizeGuideVisible)
{
parent.DrawPreviousResultResizeGuide(
resizeGuideScreenX);
}
resizePreviewDelta = delta;
resizeGuideScreenX =
resizeStartGuideScreenX + delta;
parent.DrawPreviousResultResizeGuide(
resizeGuideScreenX);
resizeGuideVisible = true;
}
void resizeHandle_MouseUp(object sender, MouseEventArgs e)
{
if (!resizing)
{
return;
}
PreviousResultResizeHandle handle =
sender as PreviousResultResizeHandle;
if (resizeGuideVisible)
{
parent.DrawPreviousResultResizeGuide(
resizeGuideScreenX);
resizeGuideVisible = false;
}
resizing = false;
if (handle != null)
{
handle.Capture = false;
}
if (resizePreviewDelta != 0)
{
parent.ResizePreviousResultZone(
this,
resizingZone,
resizePreviewDelta);
}
resizePreviewDelta = 0;
}
int GetMinimumZoneWidth(PreviousResultResizeZone zone)
{
switch (zone)
{
case PreviousResultResizeZone.BatchNr:
return 45;
case PreviousResultResizeZone.StartTime:
case PreviousResultResizeZone.EndTime:
return 95;
case PreviousResultResizeZone.SwVersion:
return 60;
case PreviousResultResizeZone.Procedure:
return 120;
case PreviousResultResizeZone.PurchaseOrder:
return 90;
case PreviousResultResizeZone.PassedCount:
case PreviousResultResizeZone.FailedCount:
return 65;
default:
return 40;
}
}
internal int LimitResizeDelta(PreviousResultResizeZone zone, int delta)
{
int index = (int)zone;
int previousBoundary =
index == 0
? 0
: GetHandleBoundary(index - 1);
int currentBoundary =
GetHandleBoundary(index);
int minimumBoundary =
previousBoundary +
GetMinimumZoneWidth(zone);
if (currentBoundary + delta < minimumBoundary)
{
delta =
minimumBoundary -
currentBoundary;
}
if (zone == PreviousResultResizeZone.Procedure)
{
int nextBoundary =
GetHandleBoundary(
(int)PreviousResultResizeZone.PurchaseOrder);
int maximumBoundary =
nextBoundary -
GetMinimumZoneWidth(
PreviousResultResizeZone.PurchaseOrder);
if (currentBoundary + delta > maximumBoundary)
{
delta =
maximumBoundary -
currentBoundary;
}
}
return delta;
}
internal void ResizeZone(PreviousResultResizeZone zone, int delta)
{
if (delta == 0)
{
return;
}
int index = (int)zone;
SuspendLayout();
int oldProcedureStretch = procedureStretch;
SetProcedureStretch(0);
if (zone == PreviousResultResizeZone.Procedure)
{
resizeHandles[index].Left += delta;
}
else
{
for (int i = index;
i < resizeHandles.Count;
i++)
{
resizeHandles[i].Left += delta;
}
minimumRowWidth += delta;
}
SetProcedureStretch(oldProcedureStretch);
UpdateRowWidth();
ResumeLayout(false);
Invalidate();
}
void UpdateFieldBounds()
{
SetFieldBounds(
batchNrLabel,
GetZoneStart(PreviousResultResizeZone.BatchNr),
GetZoneEnd(PreviousResultResizeZone.BatchNr));
SetFieldBounds(
startTimeLabel,
GetZoneStart(PreviousResultResizeZone.StartTime),
GetZoneEnd(PreviousResultResizeZone.StartTime));
SetFieldBounds(
endTimeLabel,
GetZoneStart(PreviousResultResizeZone.EndTime),
GetZoneEnd(PreviousResultResizeZone.EndTime));
SetFieldBounds(
swVerLabel,
GetZoneStart(PreviousResultResizeZone.SwVersion),
GetZoneEnd(PreviousResultResizeZone.SwVersion));
SetFieldBounds(
procedureLabel,
GetZoneStart(PreviousResultResizeZone.Procedure),
GetZoneEnd(PreviousResultResizeZone.Procedure));
SetFieldBounds(
purchaseOrderLabel,
GetZoneStart(PreviousResultResizeZone.PurchaseOrder),
GetZoneEnd(PreviousResultResizeZone.PurchaseOrder));
SetFieldBounds(
passedCountLabel,
GetZoneStart(PreviousResultResizeZone.PassedCount),
GetZoneEnd(PreviousResultResizeZone.PassedCount));
SetFieldBounds(
failedCountLabel,
GetZoneStart(PreviousResultResizeZone.FailedCount),
GetZoneEnd(PreviousResultResizeZone.FailedCount));
}
void SetFieldBounds(Label label, int left, int right)
{
const int horizontalPadding = 3;
const int top = 3;
int width =
Math.Max(
20,
right - left - (horizontalPadding * 2));
int height =
Math.Max(
18,
Height - 6);
label.SetBounds(
left + horizontalPadding,
top,
width,
height);
}
int GetZoneStart(PreviousResultResizeZone zone)
{
int index = (int)zone;
if (index == 0)
{
return 0;
}
return GetHandleBoundary(index - 1);
}
int GetZoneEnd(PreviousResultResizeZone zone)
{
return GetHandleBoundary((int)zone);
}
internal Rectangle GetZoneRectangle(PreviousResultResizeZone zone)
{
int left = GetZoneStart(zone);
int right = GetZoneEnd(zone);
return new Rectangle(
left,
0,
right - left,
Height);
}
internal int GetActionsLeft()
{
if (sentCheckBox.Visible)
{
return Math.Max(
GetZoneEnd(PreviousResultResizeZone.FailedCount),
sentCheckBox.Left - 6);
}
return Math.Max(
GetZoneEnd(PreviousResultResizeZone.FailedCount),
writeButton.Left - 6);
}
public void UpdateSentState(bool sent)
{
this.Sent = sent;
#if MUNICH
sentCheckBox.Checked = sent;
sendReloadButton.Enabled = true;
@@ -104,7 +879,7 @@ namespace TBF.UI.ResultsMI
sentCheckBox.Checked = sent;
sendReloadButton.Enabled = !sent;
#endif
}
}
private void showButton_Click(object sender, EventArgs e)
{
@@ -148,4 +923,4 @@ namespace TBF.UI.ResultsMI
parent.OnWrite(this, new PreviousResultIdEventArgs(BatchNr, SerialNrs));
}
}
}
}
+514 -133
View File
@@ -3,6 +3,7 @@
///
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using log4net;
using NHibernate;
@@ -29,31 +30,53 @@ namespace TBF.UI.ResultsMI
Fix,
}
public partial class PreviousResultsDlg : Form
{
static readonly ILog log = LogManager.GetLogger(typeof(PreviousResultsDlg));
public partial class PreviousResultsDlg : Form
{
static readonly ILog log = LogManager.GetLogger(typeof(PreviousResultsDlg));
ISession session;
int currentBatchNr;
ISession session;
int currentBatchNr;
IList<Batch> batches; /// A list of batches selected from all batches using criteria entered in UI
IList<string> serialNrs; /// Display only watermeters with these serial numbers (null = display all)
int lastDisplayedIx; /// Index of the last displayed batch from the list 'batches'
const int LinesCount = 25; /// Number of batches displayed on one screen
PreviousResultsMode mode;
readonly int[] previousResultZoneDeltas =
new int[(int)PreviousResultResizeZone.Count];
Panel previousResultsHeaderPanel;
Label[] previousResultsHeaderLabels;
readonly Timer resizeUpdateTimer;
public PreviousResultsDlg(PreviousResultsMode mode)
{
InitializeComponent();
{
InitializeComponent();
///
/// Open approximately 40% wider than the original designer size.
///
ClientSize = new Size(
(int)Math.Round(ClientSize.Width * 1.40),
ClientSize.Height);
resizeUpdateTimer = new Timer();
resizeUpdateTimer.Interval = 80;
resizeUpdateTimer.Tick += resizeUpdateTimer_Tick;
flowLayoutPanel1.SizeChanged += flowLayoutPanel1_SizeChanged;
ResizeEnd += PreviousResultsDlg_ResizeEnd;
this.Icon = Properties.Resources.TBF_icon;
this.mode = mode;
currentBatchNr = Program.LocalSettings.BatchNr;
currentBatchNr = Program.LocalSettings.BatchNr;
PrintHandler += delegate(object sndr, PreviousResultIdEventArgs args)
PrintHandler += delegate (object sndr, PreviousResultIdEventArgs args)
{
if (InvokeRequired) { Invoke(new EventHandler<PreviousResultIdEventArgs>(DoPrint), sndr, args); }
else DoPrint(sndr, args);
@@ -65,27 +88,27 @@ namespace TBF.UI.ResultsMI
else DoWrite(sndr, args);
};
SendAgainHandler += delegate(object sndr, PreviousResultIdEventArgs args)
{
if (InvokeRequired) { Invoke(new EventHandler<PreviousResultIdEventArgs>(DoSendAgain), sndr, args); }
else DoSendAgain(sndr, args);
};
SendAgainHandler += delegate (object sndr, PreviousResultIdEventArgs args)
{
if (InvokeRequired) { Invoke(new EventHandler<PreviousResultIdEventArgs>(DoSendAgain), sndr, args); }
else DoSendAgain(sndr, args);
};
ShowResultsHandler += delegate(object sndr, PreviousResultIdEventArgs args)
ShowResultsHandler += delegate (object sndr, PreviousResultIdEventArgs args)
{
if (InvokeRequired) { Invoke(new EventHandler<PreviousResultIdEventArgs>(DoShowResults), sndr, args); }
else DoShowResults(sndr, args);
};
}
public PreviousResultsDlg()
public PreviousResultsDlg()
: this(PreviousResultsMode.Show)
{
}
{
}
private void PreviousResultsDlg_Load(object sender, EventArgs e)
{
Localize();
private void PreviousResultsDlg_Load(object sender, EventArgs e)
{
Localize();
fromDateTimePicker.MinDate = Constants.MinDate;
fromDateTimePicker.MaxDate = DateTime.Today;
@@ -112,18 +135,18 @@ namespace TBF.UI.ResultsMI
batches = GetFilteredBatches(out lastDisplayedIx, out serialNrs);
UpdateButtons();
Redraw();
}
}
void Localize()
{
Text = Strings.Previous_results;
closeButton.Text = TBF.Resources.Strings.CloseBtnText;
void Localize()
{
Text = Strings.Previous_results;
closeButton.Text = TBF.Resources.Strings.CloseBtnText;
previousButton.Text = TBF.Resources.Strings.Previous;
nextButton.Text = TBF.Resources.Strings.Next;
fromLabel.Text = Strings.from;
toLabel.Text = Strings.to;
procedureLabel.Text = Strings.Procedure;
}
}
/// <summary>
/// Returns a list of batches selected using criteria entered in UI
@@ -186,23 +209,23 @@ namespace TBF.UI.ResultsMI
return rslt;
}
public void OnReload(object sender, PreviousResultIdEventArgs data)
{
foreach (var batch in batches)
{
if (batch.BatchNr == data.BatchNr)
{
TBF.UiBridge.Bridge.BatchNr = batch.BatchNr;
public void OnReload(object sender, PreviousResultIdEventArgs data)
{
foreach (var batch in batches)
{
if (batch.BatchNr == data.BatchNr)
{
TBF.UiBridge.Bridge.BatchNr = batch.BatchNr;
Program.MainWnd.ReloadProcedureComboBoxItems(batch.ProcedureName);
TBF.UiBridge.Bridge.Ui2Bench(TBF.UiBridge.UI2BenchCmd.ReloadBatch);
break;
}
}
Program.MainWnd.ReloadProcedureComboBoxItems(batch.ProcedureName);
TBF.UiBridge.Bridge.Ui2Bench(TBF.UiBridge.UI2BenchCmd.ReloadBatch);
break;
}
}
DialogResult = DialogResult.Cancel;
Close();
}
DialogResult = DialogResult.Cancel;
Close();
}
public void OnReloadAndFix(object sender, PreviousResultIdEventArgs data)
@@ -225,7 +248,7 @@ namespace TBF.UI.ResultsMI
public event EventHandler<PreviousResultIdEventArgs> PrintHandler;
public event EventHandler<PreviousResultIdEventArgs> PrintHandler;
/// <summary>
/// Called from PreviousResultCtrl when 'Print' button pressed.
@@ -241,10 +264,8 @@ namespace TBF.UI.ResultsMI
}
catch (Exception e)
{
// Log to file / internal logger
log.Error("PrintHandler(...) failed", e);
// Show message box to user
MessageBox.Show(
"Printing failed.\n\n" +
"Error: " + e.Message +
@@ -263,18 +284,26 @@ namespace TBF.UI.ResultsMI
if (b.BatchNr == data.BatchNr)
{
IList<IResultsPrinter> printers = new List<IResultsPrinter>();
foreach (var c in TBF.Rig.StateMachine.Components)
{
if (c is IResultsPrinter) printers.Add(c as IResultsPrinter);
}
var dlg = new PrintResultsSelectionDlg(printers, b);
if (dlg.ShowDialog() != DialogResult.OK) return;
TBF.Rig.IOperation printResultsOp = dlg.SelectedPrinter.ProcessResultsOp(b);
printResultsOp.Start();
TBF.Rig.Event e;
do { e = printResultsOp.Run(); }
while (e != TBF.Rig.Event.ResultsPrinted && e != TBF.Rig.Event.ErrorProcessingResults);
while (e != TBF.Rig.Event.ResultsPrinted &&
e != TBF.Rig.Event.ErrorProcessingResults);
printResultsOp.Stop();
}
}
@@ -282,9 +311,6 @@ namespace TBF.UI.ResultsMI
public event EventHandler<PreviousResultIdEventArgs> WriteHandler;
/// <summary>
/// Called from PreviousResultCtrl when 'Print' button pressed.
/// </summary>
public void OnWrite(object sender, PreviousResultIdEventArgs data) //...MF
{
if (PrintHandler == null)
@@ -296,10 +322,8 @@ namespace TBF.UI.ResultsMI
}
catch (Exception e)
{
// Log to file / internal logger
log.Error("WriteHandler(...) failed", e);
// Show message box to user
MessageBox.Show(
"Writing failed.\n\n" +
"Error: " + e.Message +
@@ -318,18 +342,26 @@ namespace TBF.UI.ResultsMI
if (b.BatchNr == data.BatchNr)
{
IList<IResultsWriter> writers = new List<IResultsWriter>();
foreach (var c in TBF.Rig.StateMachine.Components)
{
if (c is IResultsWriter) writers.Add(c as IResultsWriter);
}
var dlg = new WriteResultsSelectionDlg(writers, b);
if (dlg.ShowDialog() != DialogResult.OK) return;
TBF.Rig.IOperation writeResultsOp = dlg.SelectedWriter.ProcessResultsOp(b);
writeResultsOp.Start();
TBF.Rig.Event e;
do { e = writeResultsOp.Run(); }
while (e != TBF.Rig.Event.ResultsWritten && e != TBF.Rig.Event.ErrorProcessingResults);
while (e != TBF.Rig.Event.ResultsWritten &&
e != TBF.Rig.Event.ErrorProcessingResults);
writeResultsOp.Stop();
}
}
@@ -337,28 +369,26 @@ namespace TBF.UI.ResultsMI
public event EventHandler<PreviousResultIdEventArgs> SendAgainHandler;
/// <summary>
/// Called from PreviousResultCtrl when 'Send again' button pressed.
/// </summary>
public void OnSendAgain(object sender, PreviousResultIdEventArgs data)
{
if (SendAgainHandler == null) return;
try
{
SendAgainHandler(sender, data);
}
catch (Exception e)
{
log.Error("SendAgainHandler(...) failed", e);
}
}
public void OnSendAgain(object sender, PreviousResultIdEventArgs data)
{
if (SendAgainHandler == null) return;
public void DoSendAgain(object sender, PreviousResultIdEventArgs data)
{
foreach (var b in batches)
{
if (b.BatchNr == data.BatchNr)
{
try
{
SendAgainHandler(sender, data);
}
catch (Exception e)
{
log.Error("SendAgainHandler(...) failed", e);
}
}
public void DoSendAgain(object sender, PreviousResultIdEventArgs data)
{
foreach (var b in batches)
{
if (b.BatchNr == data.BatchNr)
{
#if ORACLE_DB
if (ProcessData.OracleDB == null)
{
@@ -367,9 +397,11 @@ namespace TBF.UI.ResultsMI
}
var dlg = new SendAgainDBAndWMSelectionForm();
if (dlg.ShowDialog() != DialogResult.OK) return;
OracleConnection conn;
switch (dlg.SelectedDB)
{
default:
@@ -380,29 +412,33 @@ namespace TBF.UI.ResultsMI
if (ProcessData.OracleDB.WriteResultsToDB(conn, b, dlg.SelectedWM, true) == Retv.OK)
{
b.RsltsSent = true; /// Result was successfully sent
session.SaveOrUpdate(b); /// Update MySQL DB
b.RsltsSent = true;
session.SaveOrUpdate(b);
session.Flush();
(sender as PreviousResultCtrl).Sent = true; /// Update UI
(sender as PreviousResultCtrl).Sent = true;
Redraw();
}
#endif
}
}
}
}
}
public event EventHandler<PreviousResultIdEventArgs> ShowResultsHandler;
public event EventHandler<PreviousResultIdEventArgs> ShowResultsHandler;
/// <summary>
/// Called from PreviousResultCtrl when 'Show' button pressed.
/// </summary>
public void OnShowResults(object sender, PreviousResultIdEventArgs data)
{
public void OnShowResults(object sender, PreviousResultIdEventArgs data)
{
if (ShowResultsHandler == null) return;
try { ShowResultsHandler(sender, data); }
catch (Exception e) { log.Error("ShowResultsHandler(...) failed", e); }
}
try
{
ShowResultsHandler(sender, data);
}
catch (Exception e)
{
log.Error("ShowResultsHandler(...) failed", e);
}
}
public void DoShowResults(object sender, PreviousResultIdEventArgs data)
{
@@ -412,34 +448,42 @@ namespace TBF.UI.ResultsMI
{
if (b.WaterMeters == null || b.WaterMeters.Count == 0)
{
return; /// No water meters in the selected batch
return;
}
/// Prepare result items
IList<WMeterRsltItemSpec> items = null;
foreach (var wm in b.WaterMeters)
{
if (!wm.Disabled && wm.WaterMeterData != null)
{
if (wm.WaterMeterData.Compound)
{
items = WMeterRsltItemSpec.FromStrArray(Program.LocalSettings.RsltItems_Screen_CombinedWM);
items = WMeterRsltItemSpec.FromStrArray(
Program.LocalSettings.RsltItems_Screen_CombinedWM);
}
else if (wm.WaterMeterData.HeatMeter)
{
items = WMeterRsltItemSpec.FromStrArray(Program.LocalSettings.RsltItems_Screen_HeatM);
items = WMeterRsltItemSpec.FromStrArray(
Program.LocalSettings.RsltItems_Screen_HeatM);
}
else
{
items = WMeterRsltItemSpec.FromStrArray(Program.LocalSettings.RsltItems_Screen_SingleWM);
items = WMeterRsltItemSpec.FromStrArray(
Program.LocalSettings.RsltItems_Screen_SingleWM);
}
break;
}
}
if (items == null) items = new List<WMeterRsltItemSpec>();
/// Show dialog with results
Results.Forms.BatchResultsDlg dlg = new Results.Forms.BatchResultsDlg
if (items == null)
{
items = new List<WMeterRsltItemSpec>();
}
Results.Forms.BatchResultsDlg dlg =
new Results.Forms.BatchResultsDlg
{
Text = string.Format(Strings.Results_of_batch_0, b.BatchNr),
Results = BatchResults.FromBatch(b),
@@ -465,54 +509,388 @@ namespace TBF.UI.ResultsMI
}
}
internal void ApplyPreviousResultLayout(PreviousResultCtrl ctrl)
{
for (PreviousResultResizeZone zone = 0;
zone < PreviousResultResizeZone.Count;
zone++)
{
int delta =
previousResultZoneDeltas[(int)zone];
if (delta != 0)
{
ctrl.ResizeZone(zone, delta);
}
}
}
internal void ResizePreviousResultZone(
PreviousResultCtrl source,
PreviousResultResizeZone zone,
int delta)
{
delta =
source.LimitResizeDelta(zone, delta);
if (delta == 0)
{
return;
}
previousResultZoneDeltas[(int)zone] += delta;
flowLayoutPanel1.SuspendLayout();
foreach (Control control in flowLayoutPanel1.Controls)
{
PreviousResultCtrl ctrl =
control as PreviousResultCtrl;
if (ctrl != null)
{
ctrl.ResizeZone(zone, delta);
}
}
UpdatePreviousResultsHeader(source);
flowLayoutPanel1.ResumeLayout(false);
}
internal void DrawPreviousResultResizeGuide(int screenX)
{
Point top =
flowLayoutPanel1.PointToScreen(Point.Empty);
Point bottom =
flowLayoutPanel1.PointToScreen(
new Point(
0,
flowLayoutPanel1.ClientSize.Height));
ControlPaint.DrawReversibleLine(
new Point(screenX, top.Y),
new Point(screenX, bottom.Y),
Color.Gray);
}
int GetPreviousResultAvailableWidth()
{
return Math.Max(
100,
flowLayoutPanel1.ClientSize.Width -
SystemInformation.VerticalScrollBarWidth -
6);
}
void flowLayoutPanel1_SizeChanged(object sender, EventArgs e)
{
///
/// Do not recalculate all rows for every pixel while
/// the window is being resized.
///
resizeUpdateTimer.Stop();
resizeUpdateTimer.Start();
}
void resizeUpdateTimer_Tick(object sender, EventArgs e)
{
resizeUpdateTimer.Stop();
UpdatePreviousResultAvailableWidth();
}
void PreviousResultsDlg_ResizeEnd(object sender, EventArgs e)
{
resizeUpdateTimer.Stop();
UpdatePreviousResultAvailableWidth();
}
void UpdatePreviousResultAvailableWidth()
{
if (flowLayoutPanel1.Controls.Count == 0)
{
return;
}
int width =
GetPreviousResultAvailableWidth();
PreviousResultCtrl firstResult = null;
flowLayoutPanel1.SuspendLayout();
foreach (Control control in flowLayoutPanel1.Controls)
{
PreviousResultCtrl ctrl =
control as PreviousResultCtrl;
if (ctrl != null)
{
ctrl.SetAvailableWidth(width);
if (firstResult == null)
{
firstResult = ctrl;
}
}
}
if (firstResult != null)
{
UpdatePreviousResultsHeader(firstResult);
}
flowLayoutPanel1.ResumeLayout(false);
}
Panel CreatePreviousResultsHeader(
PreviousResultCtrl ctrl)
{
Panel panel =
new Panel
{
Width = ctrl.Width,
Height = 29,
BackColor = Color.FromArgb(232, 238, 246),
Margin = new Padding(3, 0, 3, 1),
};
panel.Paint += delegate (object sender, PaintEventArgs e)
{
using (Pen pen =
new Pen(Color.FromArgb(203, 213, 225)))
{
e.Graphics.DrawLine(
pen,
0,
panel.Height - 1,
panel.Width,
panel.Height - 1);
}
};
previousResultsHeaderLabels =
new Label[
(int)PreviousResultResizeZone.Count + 1];
string[] captions =
{
"Batch",
"Start time",
"End time",
"SW version",
"Procedure",
"Purchase order",
Strings.Passed,
Strings.Failed,
"Actions",
};
for (int i = 0;
i < (int)PreviousResultResizeZone.Count;
i++)
{
Label label =
CreateHeaderLabel(captions[i]);
previousResultsHeaderLabels[i] = label;
panel.Controls.Add(label);
}
Label actionsLabel =
CreateHeaderLabel(
captions[
(int)PreviousResultResizeZone.Count]);
previousResultsHeaderLabels[
(int)PreviousResultResizeZone.Count] =
actionsLabel;
panel.Controls.Add(actionsLabel);
return panel;
}
Label CreateHeaderLabel(string text)
{
return new Label
{
Text = text,
AutoSize = false,
ForeColor = Color.FromArgb(51, 65, 85),
BackColor = Color.Transparent,
Font = new Font(
"Segoe UI",
9F,
FontStyle.Bold),
TextAlign = ContentAlignment.MiddleCenter,
Padding = new Padding(0),
AutoEllipsis = true,
};
}
void UpdatePreviousResultsHeader(
PreviousResultCtrl ctrl)
{
if (previousResultsHeaderPanel == null ||
previousResultsHeaderLabels == null)
{
return;
}
previousResultsHeaderPanel.Width =
ctrl.Width;
for (PreviousResultResizeZone zone = 0;
zone < PreviousResultResizeZone.Count;
zone++)
{
Rectangle bounds =
ctrl.GetZoneRectangle(zone);
previousResultsHeaderLabels[(int)zone]
.SetBounds(
bounds.Left,
0,
bounds.Width,
previousResultsHeaderPanel.Height);
}
int actionsLeft =
ctrl.GetActionsLeft();
previousResultsHeaderLabels[
(int)PreviousResultResizeZone.Count]
.SetBounds(
actionsLeft,
0,
Math.Max(
100,
ctrl.Width - actionsLeft),
previousResultsHeaderPanel.Height);
previousResultsHeaderPanel.PerformLayout();
previousResultsHeaderPanel.Invalidate(true);
}
/// <summary>
/// Redraws list of batches.
/// Uses member variables 'batches' and 'lastDisplayedIx'
/// </summary>
void Redraw()
{
SuspendLayout();
{
SuspendLayout();
flowLayoutPanel1.SuspendLayout();
flowLayoutPanel1.FlowDirection = FlowDirection.TopDown;
flowLayoutPanel1.Controls.Clear();
flowLayoutPanel1.FlowDirection =
FlowDirection.TopDown;
if (batches == null || batches.Count == 0) return;
flowLayoutPanel1.WrapContents = false;
for (int i = Math.Max(0, lastDisplayedIx - LinesCount + 1); i <= lastDisplayedIx; i++)
flowLayoutPanel1.Controls.Clear();
previousResultsHeaderPanel = null;
previousResultsHeaderLabels = null;
if (batches == null ||
batches.Count == 0)
{
var b = batches[i];
int passedCount = 0;
int failedCount = 0;
foreach (var wm in b.WaterMeters)
{
if ((wm != null) && wm.Passed) { passedCount++; } else { failedCount++; }
}
if (passedCount + failedCount == 0) continue;
flowLayoutPanel1.Controls.Add(new PreviousResultCtrl(this,
b.BatchNr,
b.StartTime,
b.EndTime,
b.ProgramVersion,
b.ProcedureName,
b.WaterMeters[0].PurchaseOrder,
passedCount,
failedCount,
b.RsltsSent,
mode,
serialNrs));
flowLayoutPanel1.ResumeLayout();
ResumeLayout();
return;
}
ResumeLayout();
}
bool headerCreated = false;
private void closeButton_Click(object sender, EventArgs e)
{
int availableWidth =
GetPreviousResultAvailableWidth();
for (int i =
Math.Max(
0,
lastDisplayedIx - LinesCount + 1);
i <= lastDisplayedIx;
i++)
{
var b = batches[i];
int passedCount = 0;
int failedCount = 0;
foreach (var wm in b.WaterMeters)
{
if ((wm != null) && wm.Passed)
{
passedCount++;
}
else
{
failedCount++;
}
}
if (passedCount + failedCount == 0)
{
continue;
}
PreviousResultCtrl ctrl =
new PreviousResultCtrl(
this,
b.BatchNr,
b.StartTime,
b.EndTime,
b.ProgramVersion,
b.ProcedureName,
b.WaterMeters[0].PurchaseOrder,
passedCount,
failedCount,
b.RsltsSent,
mode,
serialNrs);
ctrl.Margin =
new Padding(3, 0, 3, 0);
ctrl.SetAvailableWidth(
availableWidth);
if (!headerCreated)
{
previousResultsHeaderPanel =
CreatePreviousResultsHeader(ctrl);
flowLayoutPanel1.Controls.Add(
previousResultsHeaderPanel);
UpdatePreviousResultsHeader(ctrl);
headerCreated = true;
}
flowLayoutPanel1.Controls.Add(ctrl);
}
flowLayoutPanel1.ResumeLayout();
UpdatePreviousResultAvailableWidth();
ResumeLayout();
}
private void closeButton_Click(object sender, EventArgs e)
{
if (session != null) session.Close();
DialogResult = DialogResult.Cancel;
Close();
}
resizeUpdateTimer.Stop();
DialogResult = DialogResult.Cancel;
Close();
}
private void previousButton_Click(object sender, EventArgs e)
{
@@ -543,7 +921,10 @@ namespace TBF.UI.ResultsMI
private void PreviousResultsDlg_FormClosing(object sender, FormClosingEventArgs e)
{
resizeUpdateTimer.Stop();
resizeUpdateTimer.Dispose();
if (CurrentUser.Restore(this)) Program.MainWnd.UpdateUser();
}
}
}
}
}