using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using InABox.Core; using Button = System.Windows.Controls.Button; using Color = System.Windows.Media.Color; using ComboBox = System.Windows.Controls.ComboBox; using HorizontalAlignment = System.Windows.HorizontalAlignment; using Label = System.Windows.Controls.Label; using SystemColors = System.Windows.SystemColors; using TextBox = System.Windows.Controls.TextBox; using UserControl = System.Windows.Controls.UserControl; namespace InABox.WPF { public class QAGrid : UserControl { public delegate void QAFormChangedEvent(object sender, Dictionary values); private readonly Dictionary borders = new(); private readonly Grid MasterGrid; public QAGrid() { BorderBrush = new SolidColorBrush(Colors.Gray); BorderThickness = new Thickness(0.75); MasterGrid = new Grid(); MasterGrid.Background = new SolidColorBrush(Colors.WhiteSmoke); MasterGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.25F, GridUnitType.Star) }); MasterGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.25F, GridUnitType.Star) }); MasterGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Auto) }); MasterGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) }); MasterGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Auto) }); Content = MasterGrid; } public bool Preview { get; set; } public QAFormChangedEvent OnChanged { get; set; } private object GetValue(Dictionary values, Guid guid) { if (values.ContainsKey(guid)) return values[guid]; return ""; } private Border CreateHeader(string title) { var label = new Label { Content = title, FontSize = 16.0F, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; var border = new Border { CornerRadius = new CornerRadius(10.0F, 10.0F, 0.0F, 0.0F), BorderThickness = new Thickness(1.0F), BorderBrush = SystemColors.WindowFrameBrush, Background = SystemColors.WindowBrush, Margin = new Thickness(5.0F, 5.0F, 5.0F, 2.0F), Padding = new Thickness(5.0F) }; border.SetValue(Grid.ColumnProperty, MasterGrid.ColumnDefinitions.Count - 1); border.SetValue(Grid.RowProperty, 0); border.Child = label; MasterGrid.Children.Add(border); return border; } private Thickness GetMargin(Grid grid) { if (grid.RowDefinitions.Count == 0) return new Thickness(2.0F, 0.0F, 2.0F, 0.0F); return new Thickness(4.0F, 3.0F, 4.0F, 3.0F); } public static Color InterpolateColors(Color color1, Color color2, float percentage) { double a1 = color1.A / 255.0, r1 = color1.R / 255.0, g1 = color1.G / 255.0, b1 = color1.B / 255.0; double a2 = color2.A / 255.0, r2 = color2.R / 255.0, g2 = color2.G / 255.0, b2 = color2.B / 255.0; var a3 = Convert.ToByte((a1 + (a2 - a1) * percentage) * 255); var r3 = Convert.ToByte((r1 + (r2 - r1) * percentage) * 255); var g3 = Convert.ToByte((g1 + (g2 - g1) * percentage) * 255); var b3 = Convert.ToByte((b1 + (b2 - b1) * percentage) * 255); return Color.FromArgb(a3, r3, g3, b3); } private Color GetBorder(QAQuestion question, Dictionary answers) { var parameters = question.ParseParameters(); var bAnswerRequired = question.Answer != QAAnswer.Comment && (!parameters.ContainsKey("Default") || string.IsNullOrWhiteSpace(parameters["Default"])); if (bAnswerRequired && (!answers.ContainsKey(question.ID) || answers[question.ID] == null)) return Colors.Red; return Colors.Transparent; } public void CollapseMargins() { MasterGrid.ColumnDefinitions.First().Width = new GridLength(0.0F); MasterGrid.ColumnDefinitions.Last().Width = new GridLength(0.0F); } private void Changed(Dictionary values) { OnChanged?.Invoke(this, values); foreach (var question in borders.Keys) { var color = GetBorder(question, values); borders[question].BorderBrush = new SolidColorBrush(color); borders[question].Background = new SolidColorBrush(InterpolateColors(color, Colors.Transparent, 0.8F)); } } private Border CreateBorder(Grid grid, QAQuestion check, Dictionary values) { var border = new Border(); border.SetValue(Grid.ColumnProperty, 0); border.SetValue(Grid.RowProperty, grid.RowDefinitions.Count); border.Padding = new Thickness(2.0F); var color = GetBorder(check, values); border.BorderBrush = new SolidColorBrush(color); border.Background = new SolidColorBrush(InterpolateColors(color, Colors.Transparent, 0.8F)); border.BorderThickness = new Thickness(1.5F); border.Margin = new Thickness(0F, 2F, 0F, 2F); border.CornerRadius = new CornerRadius(5.0F); grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Auto) }); grid.Children.Add(border); return border; } private Border CreateCommentPanel(Grid grid, QAQuestion check, Dictionary values, int number) { var border = CreateBorder(grid, check, values); var row = new Grid(); row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) }); row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) }); var label = new TextBlock { Text = check.Question, FontSize = 14.0F, TextAlignment = TextAlignment.Center, TextWrapping = TextWrapping.Wrap, VerticalAlignment = VerticalAlignment.Center, Margin = GetMargin(grid), Padding = new Thickness(10) }; label.SetValue(Grid.RowProperty, 0); label.SetValue(Grid.ColumnProperty, 0); row.Children.Add(label); border.Child = row; return border; } private Border CreateChoicePanel(Grid grid, QAQuestion check, Dictionary values, int number) { var border = CreateBorder(grid, check, values); var row = new Grid(); row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Auto) }); row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) }); var label = new TextBlock { Text = string.Format("{0}. {1}", number, check.Question), FontSize = 14.0F, Margin = GetMargin(grid), VerticalAlignment = VerticalAlignment.Center, TextAlignment = TextAlignment.Left, TextWrapping = TextWrapping.Wrap }; label.SetValue(Grid.RowProperty, 0); label.SetValue(Grid.ColumnProperty, 0); row.Children.Add(label); if (!string.IsNullOrEmpty(check.Parameters)) { var parameters = check.ParseParameters(); var buttons = parameters["Options"].Split(','); var colors = parameters["Colors"].Split(','); var value = GetValue(values, check.ID).ToString(); if (string.IsNullOrWhiteSpace(value)) value = parameters["Default"]; for (var i = 0; i < buttons.Length; i++) if (!string.IsNullOrWhiteSpace(buttons[i])) { var brush = new SolidColorBrush(Colors.WhiteSmoke); try { brush = new BrushConverter().ConvertFromString(colors[i]) as SolidColorBrush; } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80.0F) }); var button = new Button { Content = buttons[i], FontSize = 14.0F, Margin = GetMargin(grid), Height = 40, Background = buttons[i].Equals(value) ? brush : new SolidColorBrush(Colors.WhiteSmoke), Tag = brush }; button.Click += (o, e) => { values[check.ID] = button.Content.ToString(); foreach (var other in row.Children) if (other is Button) { if (other == button) ((Button)other).Background = (SolidColorBrush)button.Tag; else ((Button)other).Background = new SolidColorBrush(Colors.WhiteSmoke); } Changed(values); }; button.SetValue(Grid.RowProperty, 0); button.SetValue(Grid.ColumnProperty, i + 1); row.Children.Add(button); } } border.Child = row; return border; } private Border CreateNumberPanel(Grid grid, QAQuestion check, Dictionary values, int number) { var border = CreateBorder(grid, check, values); var row = new Grid(); row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) }); row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) }); var label = new TextBlock { Text = string.Format("{0}. {1}", number, check.Question), FontSize = 14.0F, Margin = GetMargin(grid), VerticalAlignment = VerticalAlignment.Center, TextAlignment = TextAlignment.Left, TextWrapping = TextWrapping.Wrap }; label.SetValue(Grid.RowProperty, 0); label.SetValue(Grid.ColumnProperty, 0); row.Children.Add(label); row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(160.0F) }); var editor = new TextBox { Margin = GetMargin(grid), TextAlignment = TextAlignment.Center, VerticalContentAlignment = VerticalAlignment.Center, Height = 40, FontSize = 14.0F }; editor.SetValue(Grid.RowProperty, 0); editor.SetValue(Grid.ColumnProperty, 1); row.Children.Add(editor); var def = ""; if (!string.IsNullOrEmpty(check.Parameters)) { var parameters = check.ParseParameters(); def = parameters["Default"]; } var value = GetValue(values, check.ID).ToString(); editor.Text = string.IsNullOrWhiteSpace(value) ? def : value; editor.TextChanged += (o, e) => { values[check.ID] = editor.Text; Changed(values); }; border.Child = row; return border; } private Border CreateTextPanel(Grid grid, QAQuestion check, Dictionary values, int number) { var border = CreateBorder(grid, check, values); var row = new Grid(); row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) }); row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) }); var label = new TextBlock { Text = string.Format("{0}. {1}", number, check.Question), FontSize = 14.0F, Margin = GetMargin(grid), VerticalAlignment = VerticalAlignment.Center, TextAlignment = TextAlignment.Left, TextWrapping = TextWrapping.Wrap }; label.SetValue(Grid.RowProperty, 0); label.SetValue(Grid.ColumnProperty, 0); row.Children.Add(label); row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(320.0F, GridUnitType.Pixel) }); var editor = new TextBox { Margin = GetMargin(grid), TextAlignment = TextAlignment.Center, VerticalContentAlignment = VerticalAlignment.Center, Height = 40, FontSize = 14.0F }; editor.SetValue(Grid.RowProperty, 0); editor.SetValue(Grid.ColumnProperty, 1); row.Children.Add(editor); var def = ""; if (!string.IsNullOrEmpty(check.Parameters)) { var parameters = check.ParseParameters(); def = parameters["Default"]; } var value = GetValue(values, check.ID).ToString(); editor.Text = string.IsNullOrWhiteSpace(value) ? def : value; editor.TextChanged += (o, e) => { values[check.ID] = editor.Text; Changed(values); }; border.Child = row; return border; } private Border CreateComboPanel(Grid grid, QAQuestion check, Dictionary values, int number) { var border = CreateBorder(grid, check, values); var row = new Grid(); row.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) }); row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) }); var label = new TextBlock { Text = string.Format("{0}. {1}", number, check.Question), FontSize = 14.0F, Margin = GetMargin(grid), VerticalAlignment = VerticalAlignment.Center, TextAlignment = TextAlignment.Left, TextWrapping = TextWrapping.Wrap }; label.SetValue(Grid.RowProperty, 0); label.SetValue(Grid.ColumnProperty, 0); row.Children.Add(label); row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(320.0F, GridUnitType.Pixel) }); var editor = new ComboBox { Margin = GetMargin(grid), HorizontalContentAlignment = HorizontalAlignment.Left, VerticalContentAlignment = VerticalAlignment.Center, Height = 40 }; editor.SetValue(Grid.RowProperty, 0); editor.SetValue(Grid.ColumnProperty, 1); row.Children.Add(editor); if (!string.IsNullOrEmpty(check.Parameters)) { var parameters = check.ParseParameters(); var options = parameters["Options"].Split(','); var def = parameters["Default"]; for (var i = 0; i < options.Length; i++) editor.Items.Add(options[i]); var value = GetValue(values, check.ID).ToString(); editor.SelectedIndex = editor.Items.IndexOf(string.IsNullOrWhiteSpace(value) ? def : value); } editor.SelectionChanged += (o, e) => { values[check.ID] = editor.SelectedIndex >= 0 ? editor.Items[editor.SelectedIndex].ToString() : ""; Changed(values); }; border.Child = row; return border; } public void Clear() { borders.Clear(); MasterGrid.Children.Clear(); MasterGrid.ColumnDefinitions.Clear(); MasterGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.25F, GridUnitType.Star) }); MasterGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.25F, GridUnitType.Star) }); } public void LoadChecks(string title, IEnumerable checks, Dictionary values) { var sections = new List(); foreach (var check in checks) { var section = string.IsNullOrWhiteSpace(check.Section) ? title : check.Section; if (!sections.Contains(section)) sections.Add(section); } if (!sections.Any()) sections.Add(title); foreach (var section in sections) { var header = CreateHeader(section); var border = new Border { CornerRadius = new CornerRadius(0.0F, 0.0F, 0.0F, 0.0F), BorderThickness = new Thickness(1.0F), BorderBrush = SystemColors.WindowFrameBrush, Background = SystemColors.WindowBrush, Margin = new Thickness(5.0F, 0.0F, 5.0F, 5.0F), Padding = new Thickness(10.0F, 10.0F, 5.0F, 10.0F) }; border.SetValue(Grid.RowProperty, 1); border.SetValue(Grid.ColumnProperty, MasterGrid.ColumnDefinitions.Count - 1); if (MasterGrid.ColumnDefinitions.Count > 2) { header.Margin = new Thickness(0.0F, 5.0F, 5.0F, 2.0F); border.Margin = new Thickness(0.0F, 0.0F, 5.0F, 5.0F); CollapseMargins(); } MasterGrid.ColumnDefinitions.Insert(MasterGrid.ColumnDefinitions.Count - 1, new ColumnDefinition { Width = new GridLength(1F, GridUnitType.Star) }); MasterGrid.Children.Add(border); var scroll = new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto, HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled, Padding = new Thickness(0.0F, 0.0F, 5.0F, 0.0F) }; border.Child = scroll; var grid = new Grid(); var i = 1; if (!checks.Any()) { var check = new QAQuestion { Answer = QAAnswer.Comment, Question = "There are no checks defined for this form", Parameters = "" }; borders[check] = CreateCommentPanel(grid, check, values, i); } else { foreach (var check in checks) { Border element = null; if (check.Answer == QAAnswer.Comment) element = CreateCommentPanel(grid, check, values, i); else if (check.Answer == QAAnswer.Choice) element = CreateChoicePanel(grid, check, values, i++); else if (check.Answer == QAAnswer.Number) element = CreateNumberPanel(grid, check, values, i++); else if (check.Answer == QAAnswer.Text) element = CreateTextPanel(grid, check, values, i++); else if (check.Answer == QAAnswer.Combo) element = CreateComboPanel(grid, check, values, i++); if (element != null) borders[check] = element; } } scroll.Content = grid; } } } }