protected DataEntryControl(ControlRow control, DataEntryControls styleProvider)
        {
            // populate properties from database definition of control
            // this.Content and Tooltip can't be set, however, as the caller hasn't instantiated the content control yet
            this.Copyable = control.Copyable;
            this.DataLabel = control.DataLabel;
            this.DefaultValue = control.DefaultValue;

            // Create the stack panel
            this.Container = new StackPanel();
            Style style = styleProvider.FindResource(Constant.ControlStyle.ContainerStyle) as Style;
            this.Container.Style = style;

            // use the containers's tag to point back to this so event handlers can access the DataEntryControl
            // this is needed by callbacks such as DataEntryHandler.Container_PreviewMouseRightButtonDown() and CarnassialWindow.CounterControl_MouseLeave()
            this.Container.Tag = this;
        }
        private ComboBox CreateComboBox(DataEntryControls styleProvider, ControlRow control)
        {
            ComboBox comboBox = new ComboBox();
            comboBox.ToolTip = control.Tooltip;
            comboBox.Width = control.Width;
            foreach (string choice in control.GetChoices())
            {
                comboBox.Items.Add(choice);
            }
            comboBox.SelectedIndex = 0;

            Style style = styleProvider.FindResource(ControlContentStyle.ChoiceComboBox.ToString()) as Style;
            comboBox.Style = style;
            return comboBox;
        }
        public void Generate(EditorWindow mainWindow, WrapPanel parent, DataTableBackedList<ControlRow> templateTable)
        {
            // used for styling all content and label controls except ComboBoxes since the combo box style is commented out in DataEntryControls.xaml
            // and defined instead in MainWindow.xaml as an exception workaround
            DataEntryControls styleProvider = new DataEntryControls();

            parent.Children.Clear();
            foreach (ControlRow control in templateTable)
            {
                // instantiate control UX objects
                StackPanel stackPanel;
                switch (control.Type)
                {
                    case Constant.Control.Note:
                    case Constant.DatabaseColumn.File:
                    case Constant.DatabaseColumn.RelativePath:
                        Label noteLabel = this.CreateLabel(styleProvider, control);
                        TextBox noteContent = this.CreateTextBox(styleProvider, control);
                        stackPanel = this.CreateStackPanel(styleProvider, noteLabel, noteContent);
                        break;
                    case Constant.Control.Counter:
                        RadioButton counterLabel = this.CreateCounterLabelButton(styleProvider, control);
                        TextBox coutnerContent = this.CreateTextBox(styleProvider, control);
                        stackPanel = this.CreateStackPanel(styleProvider, counterLabel, coutnerContent);
                        break;
                    case Constant.Control.Flag:
                    case Constant.DatabaseColumn.DeleteFlag:
                        Label flagLabel = this.CreateLabel(styleProvider, control);
                        CheckBox flagContent = this.CreateFlag(styleProvider, control);
                        flagContent.IsChecked = String.Equals(control.DefaultValue, Boolean.TrueString, StringComparison.OrdinalIgnoreCase) ? true : false;
                        stackPanel = this.CreateStackPanel(styleProvider, flagLabel, flagContent);
                        break;
                    case Constant.Control.FixedChoice:
                    case Constant.DatabaseColumn.ImageQuality:
                        Label choiceLabel = this.CreateLabel(styleProvider, control);
                        ComboBox choiceContent = this.CreateComboBox(styleProvider, control);
                        stackPanel = this.CreateStackPanel(styleProvider, choiceLabel, choiceContent);
                        break;
                    case Constant.DatabaseColumn.DateTime:
                        Label dateTimeLabel = this.CreateLabel(styleProvider, control);
                        DateTimeOffsetPicker dateTimeContent = this.CreateDateTimePicker(control);
                        stackPanel = this.CreateStackPanel(styleProvider, dateTimeLabel, dateTimeContent);
                        break;
                    case Constant.DatabaseColumn.UtcOffset:
                        Label utcOffsetLabel = this.CreateLabel(styleProvider, control);
                        UtcOffsetPicker utcOffsetContent = this.CreateUtcOffsetPicker(control);
                        stackPanel = this.CreateStackPanel(styleProvider, utcOffsetLabel, utcOffsetContent);
                        break;
                    default:
                        throw new NotSupportedException(String.Format("Unhandled control type {0}.", control.Type));
                }

                stackPanel.Tag = control.DataLabel;
                if (control.Visible == false)
                {
                    stackPanel.Visibility = Visibility.Collapsed;
                }

                // add control to wrap panel
                parent.Children.Add(stackPanel);
            }
        }
        private TextBox CreateTextBox(DataEntryControls styleProvider, ControlRow control)
        {
            TextBox textBox = new TextBox();
            textBox.Text = control.DefaultValue;
            textBox.ToolTip = control.Tooltip;
            textBox.Width = control.Width;

            Style style = styleProvider.FindResource(ControlContentStyle.NoteCounterTextBox.ToString()) as Style;
            textBox.Style = style;
            return textBox;
        }
        // Returns a stack panel containing two controls
        // The stack panel ensures that controls are layed out as a single unit with certain spatial characteristcs
        // i.e.,  a given height, right margin, where contents will not be broken durring (say) panel wrapping
        private StackPanel CreateStackPanel(DataEntryControls styleProvider, Control label, Control content)
        {
            StackPanel stackPanel = new StackPanel();
            stackPanel.Children.Add(label);
            stackPanel.Children.Add(content);

            Style style = styleProvider.FindResource(Constant.ControlStyle.ContainerStyle) as Style;
            stackPanel.Style = style;
            return stackPanel;
        }
        private Label CreateLabel(DataEntryControls styleProvider, ControlRow control)
        {
            Label label = new Label();
            label.Content = control.Label;
            label.ToolTip = control.Tooltip;

            Style style = styleProvider.FindResource(ControlLabelStyle.DefaultLabel.ToString()) as Style;
            label.Style = style;
            return label;
        }
        private CheckBox CreateFlag(DataEntryControls styleProvider, ControlRow control)
        {
            CheckBox checkBox = new CheckBox();
            checkBox.Visibility = Visibility.Visible;
            checkBox.ToolTip = control.Tooltip;

            Style style = styleProvider.FindResource(ControlContentStyle.FlagCheckBox.ToString()) as Style;
            checkBox.Style = style;
            return checkBox;
        }
        private RadioButton CreateCounterLabelButton(DataEntryControls styleProvider, ControlRow control)
        {
            RadioButton radioButton = new RadioButton();
            radioButton.GroupName = "DataEntryCounter";
            radioButton.Content = control.Label;
            radioButton.ToolTip = control.Tooltip;

            Style style = styleProvider.FindResource(ControlLabelStyle.CounterButton.ToString()) as Style;
            radioButton.Style = style;
            return radioButton;
        }
        public void CreateReuseControlsAndPropagate()
        {
            List<DatabaseExpectations> databaseExpectations = new List<DatabaseExpectations>()
            {
                new DatabaseExpectations()
                {
                    FileName = Constant.File.DefaultFileDatabaseFileName,
                    TemplateDatabaseFileName = TestConstant.File.DefaultTemplateDatabaseFileName,
                    ExpectedColumns = TestConstant.DefaultFileDataColumns,
                    ExpectedControls = TestConstant.DefaultFileDataColumns.Count - 6
                }
            };

            foreach (DatabaseExpectations databaseExpectation in databaseExpectations)
            {
                FileDatabase fileDatabase = this.CreateFileDatabase(databaseExpectation.TemplateDatabaseFileName, databaseExpectation.FileName);
                DataEntryHandler dataHandler = new DataEntryHandler(fileDatabase);

                DataEntryControls controls = new DataEntryControls();
                controls.CreateControls(fileDatabase, dataHandler);
                Assert.IsTrue(controls.ControlsByDataLabel.Count == databaseExpectation.ExpectedControls, "Expected {0} controls to be generated but {1} were.", databaseExpectation.ExpectedControls, controls.ControlsByDataLabel.Count);

                // check copies aren't possible when the image enumerator's not pointing to an image
                foreach (DataEntryControl control in controls.Controls)
                {
                    Assert.IsFalse(dataHandler.IsCopyForwardPossible(control));
                    Assert.IsFalse(dataHandler.IsCopyFromLastNonEmptyValuePossible(control));
                }

                // check only copy forward is possible when enumerator's on first image
                List<FileExpectations> fileExpectations = this.PopulateDefaultDatabase(fileDatabase);
                Assert.IsTrue(dataHandler.ImageCache.MoveNext());

                List<DataEntryControl> copyableControls = controls.Controls.Where(control => control.Copyable).ToList();
                foreach (DataEntryControl control in copyableControls)
                {
                    Assert.IsTrue(dataHandler.IsCopyForwardPossible(control));
                    Assert.IsFalse(dataHandler.IsCopyFromLastNonEmptyValuePossible(control));
                }

                List<DataEntryControl> notCopyableControls = controls.Controls.Where(control => control.Copyable == false).ToList();
                foreach (DataEntryControl control in notCopyableControls)
                {
                    Assert.IsFalse(dataHandler.IsCopyForwardPossible(control));
                    Assert.IsFalse(dataHandler.IsCopyFromLastNonEmptyValuePossible(control));
                }

                // check only copy last is possible when enumerator's on last image
                // check also copy last is not possible if no previous instance of the field has been filled out
                while (dataHandler.ImageCache.CurrentRow < fileExpectations.Count - 1)
                {
                    Assert.IsTrue(dataHandler.ImageCache.MoveNext());
                }

                foreach (DataEntryControl control in copyableControls)
                {
                    Assert.IsFalse(dataHandler.IsCopyForwardPossible(control));
                    if (control.DataLabel == TestConstant.CarnivoreDatabaseColumn.Pelage ||
                        control.DataLabel == TestConstant.DefaultDatabaseColumn.ChoiceNotVisible ||
                        control.DataLabel == TestConstant.DefaultDatabaseColumn.ChoiceWithCustomDataLabel ||
                        control.DataLabel == TestConstant.DefaultDatabaseColumn.Choice3 ||
                        control.DataLabel == TestConstant.DefaultDatabaseColumn.Counter3 ||
                        control.DataLabel == TestConstant.DefaultDatabaseColumn.NoteWithCustomDataLabel)
                    {
                        Assert.IsFalse(dataHandler.IsCopyFromLastNonEmptyValuePossible(control));
                    }
                    else
                    {
                        Assert.IsTrue(dataHandler.IsCopyFromLastNonEmptyValuePossible(control));
                    }
                }

                // propagation methods not covered due to requirement of UX interaction
                // dataHandler.CopyForward(control);
                // dataHandler.CopyFromLastValue(control);
                // dataHandler.CopyToAll(control);

                // verify roundtrip of fields subject to copy/paste and analysis assignment
                Assert.IsTrue(dataHandler.ImageCache.TryMoveToFile(0));
                ImageRow firstFile = fileDatabase.Files[0];
                FileExpectations firstFileExpectations = fileExpectations[0];

                Dictionary<string, object> firstFileValuesByDataLabel = firstFile.AsDictionary();
                Assert.IsTrue(firstFileValuesByDataLabel.Count == databaseExpectation.ExpectedColumns.Count);
                foreach (KeyValuePair<string, object> fileValue in firstFileValuesByDataLabel)
                {
                    DataEntryControl control;
                    if (controls.ControlsByDataLabel.TryGetValue(fileValue.Key, out control))
                    {
                        control.SetValue(firstFileValuesByDataLabel[fileValue.Key]);
                    }
                }

                TimeZoneInfo imageSetTimeZone = fileDatabase.ImageSet.GetTimeZone();
                firstFileExpectations.Verify(firstFile, imageSetTimeZone);

                // verify roundtrip of fields via display string
                foreach (KeyValuePair<string, DataEntryControl> control in controls.ControlsByDataLabel)
                {
                    string displayString = firstFile.GetValueDisplayString(control.Value);
                    control.Value.SetContentAndTooltip(displayString);
                }

                firstFileExpectations.Verify(firstFile, imageSetTimeZone);

                // verify availability of database strings
                foreach (string dataLabel in databaseExpectation.ExpectedColumns)
                {
                    string databaseString = firstFile.GetValueDatabaseString(dataLabel);
                }
            }
        }