Beispiel #1
0
        // Return true if there is a non-empty value available
        public bool IsCopyFromLastNonEmptyValuePossible(DataEntryControl control)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(control, nameof(control));

            bool checkCounter = control is DataEntryCounter;
            bool checkFlag    = control is DataEntryFlag;
            int  nearestRowWithCopyableValue = -1;

            for (int fileIndex = this.ImageCache.CurrentRow - 1; fileIndex >= 0; fileIndex--)
            {
                // Search for the row with some value in it, starting from the previous row
                string valueToCopy = this.FileDatabase.FileTable[fileIndex].GetValueDatabaseString(control.DataLabel);
                if (String.IsNullOrWhiteSpace(valueToCopy) == false)
                {
                    // for flags, we skip over falses
                    // for counters, we skip over 0
                    // for all others, any value will work as long as its not null or white space
                    if ((checkFlag && !valueToCopy.Equals("false")) ||
                        (checkCounter && !valueToCopy.Equals("0")) ||
                        (!checkCounter && !checkFlag))
                    {
                        nearestRowWithCopyableValue = fileIndex;    // We found a non-empty value
                        break;
                    }
                }
            }
            return(nearestRowWithCopyableValue >= 0);
        }
Beispiel #2
0
        // Checks whether the image is completely black
        public static unsafe bool IsBlack(this WriteableBitmap image)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(image, nameof(image));

            // The RGB offsets from the beginning of the pixel (i.e., 0, 1 or 2)
            WriteableBitmapExtensions.GetColorOffsets(image, out int blueOffset, out int greenOffset, out int redOffset);

            // examine only a subset of pixels as otherwise this is an expensive operation
            // check pixels from last to first as most cameras put a non-black status bar or at least non-black text at the bottom of the frame,
            // so reverse order may be a little faster on average in cases of nighttime images with black skies
            // TODO  Calculate pixelStride as a function of image size so future high res images will still be processed quickly.
            byte *currentPixel = (byte *)image.BackBuffer.ToPointer(); // the imageIndex will point to a particular byte in the pixel array
            int   pixelStride  = Constant.ImageValues.DarkPixelSampleStrideDefault;
            int   totalPixels  = image.PixelHeight * image.PixelWidth; // total number of pixels in the image

            for (int pixelIndex = totalPixels - 1; pixelIndex > 0; pixelIndex -= pixelStride)
            {
                // get next pixel of interest
                byte b = *(currentPixel + blueOffset);
                byte g = *(currentPixel + greenOffset);
                byte r = *(currentPixel + redOffset);

                if (r != 0 || b != 0 || g != 0)
                {
                    return(false);
                }
            }
            return(true);
        }
Beispiel #3
0
        public DateTimeSetTimeZone(FileDatabase fileDatabase, ImageRow imageToCorrect, Window owner)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(fileDatabase, nameof(fileDatabase));
            ThrowIf.IsNullArgument(imageToCorrect, nameof(imageToCorrect));

            this.InitializeComponent();
            this.displayingPreview = false;
            this.fileDatabase      = fileDatabase;
            this.Owner             = owner;

            // get the image's current time
            DateTimeOffset currentDateTime = imageToCorrect.DateTimeIncorporatingOffset;

            this.originalDate.Content = DateTimeHandler.ToStringDisplayDateTimeUtcOffset(currentDateTime);

            // get the image filename and display it
            this.FileName.Content = imageToCorrect.File;
            this.FileName.ToolTip = this.FileName.Content;

            // display the image
            this.image.Source = imageToCorrect.LoadBitmap(this.fileDatabase.FolderPath, out bool isCorruptOrMissing);

            // configure timezone picker
            TimeZoneInfo imageSetTimeZone = this.fileDatabase.ImageSet.GetSystemTimeZone();

            this.TimeZones.SelectedItem      = imageSetTimeZone.DisplayName;
            this.TimeZones.SelectionChanged += this.TimeZones_SelectionChanged;
        }
        // Copy the current value of this control to all images
        protected virtual void MenuItemCopyCurrentValueToAll_Click(object sender, RoutedEventArgs e)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(sender, nameof(sender));

            // Get the chosen data entry control
            DataEntryControl control = (DataEntryControl)((MenuItem)sender).Tag;

            if (control == null)
            {
                return;
            }

            // Display a dialog box that explains what will happen. Arguments indicate how many files will be affected, and is tuned to the type of control
            bool checkForZero  = control is DataEntryCounter;
            int  filesAffected = this.FileDatabase.CountAllCurrentlySelectedFiles;

            if (Dialogs.DataEntryConfirmCopyCurrentValueToAllDialog(Application.Current.MainWindow, control.Content, filesAffected, checkForZero) != true)
            {
                return;
            }

            // Update all files to match the value of the control (identified by the data label) in the currently selected image row.
            Mouse.OverrideCursor = Cursors.Wait;
            ImageRow imageRow = (this.ThumbnailGrid.IsVisible == false) ? this.ImageCache.Current : this.FileDatabase.FileTable[this.ThumbnailGrid.GetSelected()[0]];

            this.FileDatabase.UpdateFiles(imageRow, control.DataLabel);
            Mouse.OverrideCursor = null;
        }
 public PopulateFieldWithEpisodeData(Window owner, FileDatabase fileDatabase) : base(owner)
 {
     ThrowIf.IsNullArgument(fileDatabase, nameof(fileDatabase));
     this.dataLabelByLabel = new Dictionary <string, string>();
     this.fileDatabase     = fileDatabase;
     this.InitializeComponent();
 }
        public ChooseFileDatabaseFile(string[] fileDatabasePaths, string templateDatabasePath, Window owner)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(fileDatabasePaths, nameof(fileDatabasePaths));

            this.InitializeComponent();
            this.Owner        = owner;
            this.SelectedFile = String.Empty;

            // file_names contains an array of .ddb files. We add each to the listbox.
            // by default, the first item in the listbox is shown selected.
            int    defaultDatabaseIndex = 0;
            string templateDatabaseNameWithoutExtension = Path.GetFileNameWithoutExtension(templateDatabasePath);

            for (int index = 0; index < fileDatabasePaths.Length; ++index)
            {
                string databaseName = Path.GetFileName(fileDatabasePaths[index]);
                string databaseNameWithoutExtension = Path.GetFileNameWithoutExtension(databaseName);
                if (String.Equals(databaseNameWithoutExtension, templateDatabaseNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
                {
                    defaultDatabaseIndex = index;
                }
                this.FileDatabases.Items.Add(databaseName);
            }
            this.FileDatabases.SelectedIndex = defaultDatabaseIndex;
        }
Beispiel #7
0
        // Return the index of this row in the dataTable
        public int GetIndex(DataTable dataTable)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(dataTable, nameof(dataTable));

            return(dataTable.Rows.IndexOf(this.Row));
        }
        // ColumnTuple: columnName = field
        public void SetWhere(ColumnTuple columnTuple, string field)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(columnTuple, nameof(columnTuple));

            this.Where = String.Format("{0} = {1}", columnTuple.Name, Sql.Quote(field));
        }
        // Paste the contents of the clipboard into the current or selected controls
        // Note that we don't do any checks against the control's type, as that would be handled by the menu enablement
        protected virtual void MenuItemPasteFromClipboard_Click(object sender, RoutedEventArgs e)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(sender, nameof(sender));

            // Get the chosen data entry control
            DataEntryControl control = (DataEntryControl)((MenuItem)sender).Tag;

            if (control == null)
            {
                return;
            }
            string newContent = Clipboard.GetText().Trim();

            if (control is DataEntryCounter _)
            {
                // For counters, removing any leading 0's, but if this ends up with an empty string, then revert to 0
                newContent = newContent.TrimStart(new Char[] { '0' });
                if (string.IsNullOrEmpty(newContent))
                {
                    newContent = "0";
                }
            }
            control.SetContentAndTooltip(newContent);
            this.UpdateRowsDependingOnThumbnailGridState(control.DataLabel, newContent);
        }
Beispiel #10
0
        public void SetSortTerm(SortTerm sortTerm1, SortTerm sortTerm2)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(sortTerm1, nameof(sortTerm1));
            ThrowIf.IsNullArgument(sortTerm2, nameof(sortTerm2));

            this.SortTerms = String.Join(",", sortTerm1.DataLabel, sortTerm1.DisplayLabel, sortTerm1.ControlType, sortTerm1.IsAscending, sortTerm2.DataLabel, sortTerm2.DisplayLabel, sortTerm2.ControlType, sortTerm2.IsAscending);
        }
        // Create the interface
        public DateTimeLinearCorrection(Window owner, FileDatabase fileDatabase) : base(owner)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(fileDatabase, nameof(fileDatabase));

            this.InitializeComponent();
            this.fileDatabase = fileDatabase;
        }
Beispiel #12
0
        public BoundingBox(string coordinates, float confidence, string detectionCategory, string detectionLabel, List <KeyValuePair <string, string> > classifications)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(coordinates, nameof(coordinates));

            float[] coords = Array.ConvertAll(coordinates.Split(','), s => float.Parse(s, CultureInfo.InvariantCulture));
            this.SetValues(coords[0], coords[1], coords[2], coords[3], confidence, detectionCategory, detectionLabel, classifications);
        }
Beispiel #13
0
        // Delete the quickPasteEntry from the quickPasteEntries list
        public static List <QuickPasteEntry> DeleteQuickPasteEntry(List <QuickPasteEntry> quickPasteEntries, QuickPasteEntry quickPasteEntry)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(quickPasteEntries, nameof(quickPasteEntries));

            quickPasteEntries.RemoveAll(x => x.Equals(quickPasteEntry));
            return(quickPasteEntries);
        }
        /// <summary>
        /// Given a ControlRow (i.e., a template row definitions) construct a column for its data based on the
        /// - the control type (Note, Date, File etc)
        /// - its DataLabel
        /// </summary>
        /// <param name="control"></param>
        protected FileTableColumn(ControlRow control)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(control, nameof(control));

            this.ControlType = control.Type;
            this.DataLabel   = control.DataLabel;
        }
Beispiel #15
0
        public FileTableChoiceColumn(ControlRow control)
            : base(control)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(control, nameof(control));

            this.choices      = control.GetChoices(false);
            this.defaultValue = control.DefaultValue;
        }
Beispiel #16
0
        public DateTimeOffset GetDateTime(int dateTimeSearchTermIndex, TimeZoneInfo imageSetTimeZone)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(imageSetTimeZone, nameof(imageSetTimeZone));

            DateTime dateTime = this.SearchTerms[dateTimeSearchTermIndex].GetDateTime();

            return(DateTimeHandler.FromDatabaseDateTimeIncorporatingOffset(dateTime, imageSetTimeZone.GetUtcOffset(dateTime)));
        }
Beispiel #17
0
        public ImageRow NewRow(FileInfo file)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(file, nameof(file));

            DataRow row = this.DataTable.NewRow();

            row[Constant.DatabaseColumn.File] = file.Name;
            return(FileTable.CreateRow(row));
        }
        public ExceptionShutdownDialog(Window owner, string programName, UnhandledExceptionEventArgs unhandledExceptionArgs)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(unhandledExceptionArgs, nameof(unhandledExceptionArgs));

            this.Owner                  = owner;
            this.ProgramName            = programName;
            this.UnhandledExceptionArgs = unhandledExceptionArgs;
            InitializeComponent();
        }
        public DateTimeFixedCorrection(Window owner, FileDatabase fileDatabase, ImageRow imageToCorrect) : base(owner)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(fileDatabase, nameof(fileDatabase));
            ThrowIf.IsNullArgument(imageToCorrect, nameof(imageToCorrect));

            this.InitializeComponent();
            this.fileDatabase   = fileDatabase;
            this.ImageToCorrect = imageToCorrect;
        }
        public DateCorrectAmbiguous(Window owner, FileDatabase fileDatabase) : base(owner)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(fileDatabase, nameof(fileDatabase));

            this.InitializeComponent();
            this.fileDatabase                  = fileDatabase;
            this.ambiguousDatesList            = new List <AmbiguousDateRange>();
            this.DateChangeFeedback.FolderPath = fileDatabase.FolderPath;
        }
        public static DateTime GetDateTimeField(this DataRow row, string column)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(row, nameof(row));

            DateTime dateTime = (DateTime)row[column];

            Debug.Assert(dateTime.Kind == DateTimeKind.Utc, String.Format("Unexpected kind {0} for date time {1}.", dateTime.Kind, dateTime));
            return(dateTime);
        }
Beispiel #22
0
        public static List <SortTerm> GetSortTerms(List <SearchTerm> searchTerms)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(searchTerms, nameof(searchTerms));

            List <SortTerm> sortTerms = new List <SortTerm>();

            // Constraints.
            // - the SearchTerms list excludes Id, Date, Time and Folder. It also includes two DateTime copies of DateTime
            // - Add Id
            // - Exclude Date and Time (as we only use DateTime): although the UTC Offset is not calculated in. While limiting, we suspect that users will not care in practice.
            // - Exclude UTCOffset, as that would involve UTCOffset complication.
            // - Remove the 2nd DateTiime
            // - Add Id as it is missing
            bool firstDateTimeSeen = false;

            sortTerms.Add(new SortTerm(Constant.DatabaseColumn.ID, Constant.DatabaseColumn.ID, Sql.IntegerType, Constant.BooleanValue.True));

            foreach (SearchTerm searchTerm in searchTerms)
            {
                // Necessary modifications:
                // - Exclude UtcOffset, RelativePath
                // - Exclude Date, Time, Folder (they shouldn't be in the SearchTerm list, but just in case)
                if (searchTerm.DataLabel == Constant.DatabaseColumn.Folder ||
                    searchTerm.DataLabel == Constant.DatabaseColumn.Date ||
                    searchTerm.DataLabel == Constant.DatabaseColumn.Time ||
                    searchTerm.DataLabel == Constant.DatabaseColumn.UtcOffset)
                {
                    continue;
                }
                if (searchTerm.DataLabel == Constant.DatabaseColumn.File)
                {
                    sortTerms.Add(new SortTerm(searchTerm.DataLabel, Constant.SortTermValues.FileDisplayLabel, searchTerm.ControlType, Constant.BooleanValue.True));
                }
                else if (searchTerm.DataLabel == Constant.DatabaseColumn.DateTime)
                {
                    // Skip the second DateTime
                    if (firstDateTimeSeen == true)
                    {
                        continue;
                    }
                    firstDateTimeSeen = true;
                    sortTerms.Add(new SortTerm(searchTerm.DataLabel, Constant.SortTermValues.DateDisplayLabel, searchTerm.ControlType, Constant.BooleanValue.True));
                }
                if (searchTerm.DataLabel == Constant.DatabaseColumn.RelativePath)
                {
                    sortTerms.Add(new SortTerm(searchTerm.DataLabel, Constant.SortTermValues.RelativePathDisplayLabel, searchTerm.ControlType, Constant.BooleanValue.True));
                }
                else
                {
                    sortTerms.Add(new SortTerm(searchTerm.DataLabel, searchTerm.Label, searchTerm.ControlType, Constant.BooleanValue.True));
                }
            }
            return(sortTerms);
        }
Beispiel #23
0
        /// <summary>
        /// Check if a synchronization between the given control row and this instance's control row is needed,
        /// which wojld occur if any field differs.
        /// Note: As a side effect it also re-orders this instance's ControlOrder and SpreadsheetOrder to the other's order if needed
        /// </summary>
        /// <param name="controlRowToMatch"></param>
        /// <returns></returns>
        public bool TryUpdateThisControlRowToMatch(ControlRow controlRowToMatch)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(controlRowToMatch, nameof(controlRowToMatch));

            bool synchronizationMadeChanges = false;

            if (this.Copyable != controlRowToMatch.Copyable)
            {
                this.Copyable = controlRowToMatch.Copyable;
                synchronizationMadeChanges = true;
            }
            if (this.ControlOrder != controlRowToMatch.ControlOrder)
            {
                this.ControlOrder          = controlRowToMatch.ControlOrder;
                synchronizationMadeChanges = true;
            }
            if (this.DefaultValue != controlRowToMatch.DefaultValue)
            {
                this.DefaultValue          = controlRowToMatch.DefaultValue;
                synchronizationMadeChanges = true;
            }
            if (this.Label != controlRowToMatch.Label)
            {
                this.Label = controlRowToMatch.Label;
                synchronizationMadeChanges = true;
            }
            if (this.List != controlRowToMatch.List)
            {
                this.List = controlRowToMatch.List;
                synchronizationMadeChanges = true;
            }
            if (this.SpreadsheetOrder != controlRowToMatch.SpreadsheetOrder)
            {
                this.SpreadsheetOrder      = controlRowToMatch.SpreadsheetOrder;
                synchronizationMadeChanges = true;
            }
            if (this.Tooltip != controlRowToMatch.Tooltip)
            {
                this.Tooltip = controlRowToMatch.Tooltip;
                synchronizationMadeChanges = true;
            }
            if (this.Visible != controlRowToMatch.Visible)
            {
                this.Visible = controlRowToMatch.Visible;
                synchronizationMadeChanges = true;
            }
            if (this.Width != controlRowToMatch.Width)
            {
                this.Width = controlRowToMatch.Width;
                synchronizationMadeChanges = true;
            }
            return(synchronizationMadeChanges);
        }
Beispiel #24
0
        public DateDaylightSavingsTimeCorrection(Window owner, FileDatabase database, FileTableEnumerator fileEnumerator) : base(owner)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(database, nameof(database));
            ThrowIf.IsNullArgument(fileEnumerator, nameof(fileEnumerator));

            this.InitializeComponent();
            this.fileDatabase    = database;
            this.fileEnumerator  = fileEnumerator;
            this.currentImageRow = fileEnumerator.CurrentRow;
        }
Beispiel #25
0
            public ImageQuality(ImageRow image)
            {
                // Check the arguments for null
                ThrowIf.IsNullArgument(image, nameof(image));

                this.Bitmap = null;
                this.DarkPixelRatioFound = 0;
                this.FileName            = image.File;
                this.IsColor             = false;
                this.OldImageQuality     = image.ImageQuality;
                this.NewImageQuality     = null;
            }
        public DateTimeRereadFromFiles(Window owner, FileDatabase fileDatabase) : base(owner)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(fileDatabase, nameof(fileDatabase));

            this.InitializeComponent();

            this.fileDatabase = fileDatabase;

            // Tracks whether any changes to the data or database are made
            this.IsAnyDataUpdated = false;
        }
Beispiel #27
0
        // Given three images, return an image that highlights the differences in common betwen the main image and the first image
        // and the main image and a second image.
        public static unsafe WriteableBitmap CombinedDifference(this WriteableBitmap unaltered, WriteableBitmap previous, WriteableBitmap next, byte threshold)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(unaltered, nameof(unaltered));
            ThrowIf.IsNullArgument(previous, nameof(previous));
            ThrowIf.IsNullArgument(next, nameof(next));

            if (WriteableBitmapExtensions.BitmapsMismatched(unaltered, previous) ||
                WriteableBitmapExtensions.BitmapsMismatched(unaltered, next))
            {
                return(null);
            }

            WriteableBitmapExtensions.GetColorOffsets(unaltered, out int blueOffset, out int greenOffset, out int redOffset);

            int   totalPixels      = unaltered.PixelWidth * unaltered.PixelHeight;
            int   pixelSizeInBytes = unaltered.Format.BitsPerPixel / 8;
            byte *unalteredIndex   = (byte *)unaltered.BackBuffer.ToPointer();
            byte *previousIndex    = (byte *)previous.BackBuffer.ToPointer();
            byte *nextIndex        = (byte *)next.BackBuffer.ToPointer();

            byte[] differencePixels = new byte[totalPixels * pixelSizeInBytes];
            int    differenceIndex  = 0;

            for (int pixel = 0; pixel < totalPixels; ++pixel)
            {
                byte b1 = (byte)Math.Abs(*(unalteredIndex + blueOffset) - *(previousIndex + blueOffset));
                byte g1 = (byte)Math.Abs(*(unalteredIndex + greenOffset) - *(previousIndex + greenOffset));
                byte r1 = (byte)Math.Abs(*(unalteredIndex + redOffset) - *(previousIndex + redOffset));

                byte b2 = (byte)Math.Abs(*(unalteredIndex + blueOffset) - *(nextIndex + blueOffset));
                byte g2 = (byte)Math.Abs(*(unalteredIndex + greenOffset) - *(nextIndex + greenOffset));
                byte r2 = (byte)Math.Abs(*(unalteredIndex + redOffset) - *(nextIndex + redOffset));

                byte b = WriteableBitmapExtensions.DifferenceIfAboveThreshold(threshold, b1, b2);
                byte g = WriteableBitmapExtensions.DifferenceIfAboveThreshold(threshold, g1, g2);
                byte r = WriteableBitmapExtensions.DifferenceIfAboveThreshold(threshold, r1, r2);

                byte averageDifference = (byte)((b + g + r) / 3);
                differencePixels[differenceIndex + blueOffset]  = averageDifference;
                differencePixels[differenceIndex + greenOffset] = averageDifference;
                differencePixels[differenceIndex + redOffset]   = averageDifference;

                unalteredIndex  += pixelSizeInBytes;
                previousIndex   += pixelSizeInBytes;
                nextIndex       += pixelSizeInBytes;
                differenceIndex += pixelSizeInBytes;
            }

            WriteableBitmap difference = new WriteableBitmap(BitmapSource.Create(unaltered.PixelWidth, unaltered.PixelHeight, unaltered.DpiX, unaltered.DpiY, unaltered.Format, unaltered.Palette, differencePixels, unaltered.BackBufferStride));

            return(difference);
        }
        public DateTimeRereadFromSelectedMetadataField(Window owner, FileDatabase fileDatabase, string filePath) : base(owner)
        {
            ThrowIf.IsNullArgument(fileDatabase, nameof(fileDatabase));

            this.InitializeComponent();
            this.fileDatabase = fileDatabase;
            this.filePath     = filePath;

            // Store various states which will eventually be reset by the user
            this.metadataFieldName     = String.Empty;
            this.metadataFieldSelected = false;
            this.noMetadataAvailable   = true;
        }
        public static void Configure(DateTimePicker dateTimePicker, Nullable <DateTime> defaultValue)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(dateTimePicker, nameof(dateTimePicker));

            dateTimePicker.AutoCloseCalendar = true;
            dateTimePicker.Format            = DateTimeFormat.Custom;
            dateTimePicker.FormatString      = Constant.Time.DateTimeDisplayFormat;
            dateTimePicker.TimeFormat        = DateTimeFormat.Custom;
            dateTimePicker.TimeFormatString  = Constant.Time.TimeFormat;
            dateTimePicker.CultureInfo       = System.Globalization.CultureInfo.CreateSpecificCulture("en-US");
            dateTimePicker.Value             = defaultValue;
        }
Beispiel #30
0
        // Copy the  value of the current control to the clipboard
        protected virtual void MenuItemCopyToClipboard_Click(object sender, RoutedEventArgs e)
        {
            // Check the arguments for null
            ThrowIf.IsNullArgument(sender, nameof(sender));

            // Get the chosen data entry control
            DataEntryControl control = (DataEntryControl)((MenuItem)sender).Tag;

            if (control == null)
            {
                return;
            }
            Clipboard.SetText(control.Content);
        }