Inheritance: ModelBase, IDisposable
        public ImageResizeDialog(SourceImage item)
        {
            this.item = item;
            this.DataContext = item;

            InitializeComponent();
        }
        public override async void Invoke()
        {
            VSITEMSELECTION sel = this.monitorSelection.GetSelectedSourceImage();
            if (sel.pHier != null)
            {
                try
                {
                    using (SourceImage source = new SourceImage(sel))
                    {
                        TelemetryHelpers.TrackDialogOpen(source);
                        ImageResizeDialog dialog = new ImageResizeDialog(source);
                        bool? result = dialog.ShowModal();

                        if (result == true)
                        {
                            TelemetryHelpers.TrackDialogOk(source);
                            await GenerateImagesMainThread(source);
                            TelemetryHelpers.TrackGenerateSuccess(source);
                        }
                        else
                        {
                            TelemetryHelpers.TrackDialogCancel(source);
                        }
                    }

                    TelemetryHelpers.Flush();
                }
                catch (Exception ex)
                {
                    TelemetryHelpers.TrackException(ex);
                }
            }
        }
        public OutputSet(SourceImage owner, string name)
        {
            this.owner = owner;
            this.name = name;
            this.description = string.Empty;
            this.expanded = true;
            this.transformType = ImageTransformType.None;
            this.outputType = ImageHelpers.IsBitmapType(owner.Image.FileType)
                ? owner.Image.FileType
                : ImageFileType.DefaultRasterize;
            this.images = new ObservableCollection<OutputImage>();

            this.UpdateSize();

            owner.PropertyChanged += this.OnOwnerPropertyChanged;
        }
        private async Task GenerateImagesMainThread(SourceImage source)
        {
            CommonMessagePump pump = new CommonMessagePump();
            pump.AllowCancel = true;
            pump.WaitTitle = "Creating scaled images";
            pump.WaitText = "Adding to the project takes time...";

            CancellationTokenSource tokenSource = new CancellationTokenSource();
            CancellationToken token = tokenSource.Token;

            Task task = Task.Run(() => GenerateImagesBackgroundThread(source, token, pump), token);

            CommonMessagePumpExitCode code = pump.ModalWaitForHandles(((IAsyncResult)task).AsyncWaitHandle);
            tokenSource.Cancel();
            await task;
        }
        public static void TrackDialogOk(SourceImage source)
        {
            if (ImageResizePackage.Instance.TelemetryClient == null)
            {
                return;
            }

            Dictionary<string, string> eventProps = new Dictionary<string, string>()
            {
                [nameof(source.Image.FileType)] = source.Image.FileType.ToString(),
                [nameof(source.Feature)] = source.Feature.Name,
                ["SetCount"] = source.SetsToGenerate.Count().ToString(),
                ["ImageCount"] = source.ImagesToGenerate.Count().ToString(),
                ["OptionalScales10"] = source.ShowOptionalScales10.ToString(),
            };

            ImageResizePackage.Instance.TelemetryClient.TrackEvent("ImageResizeDialogOk", eventProps);
        }
        public static void TrackDialogOpen(SourceImage source)
        {
            if (ImageResizePackage.Instance.TelemetryClient == null)
            {
                return;
            }

            Dictionary<string, string> eventProps = new Dictionary<string, string>()
            {
                [nameof(source.Image.FileType)] = source.Image.FileType.ToString(),
                [nameof(source.CustomPixelHeight)] = source.CustomPixelHeight.ToString(),
                [nameof(source.CustomPixelWidth)] = source.CustomPixelWidth.ToString(),
                [nameof(source.FrameHasPixelSize)] = source.FrameHasPixelSize.ToString(),
                [nameof(source.FramePixelWidth)] = source.FramePixelWidth.ToString(),
                [nameof(source.FramePixelHeight)] = source.FramePixelHeight.ToString(),
                [nameof(source.HasCustomSize)] = source.HasCustomSize.ToString(),
                [nameof(source.Scale)] = source.Scale.ToString(),
                [nameof(source.ScaleReadOnly)] = source.ScaleReadOnly.ToString(),
            };

            ImageResizePackage.Instance.TelemetryClient.TrackEvent("ShowImageResizeDialog", eventProps);
        }
        private void GenerateImagesBackgroundThread(SourceImage source, CancellationToken token, CommonMessagePump mainThreadPump)
        {
            int totalSets = source.SetsToGenerate.Count();
            int curSet = 0;

            foreach (OutputSet set in source.SetsToGenerate)
            {
                if (!token.IsCancellationRequested)
                {
                    curSet++;
                    mainThreadPump.WaitText = $"Checking existing images ({curSet} of {totalSets}):\r\n{set.UnscaledPath}";
                    this.OnGeneratingSet(set);
                }
            }

            int totalImages = source.ImagesToGenerate.Count();
            int curImage = 0;

            foreach (OutputImage image in source.ImagesToGenerate)
            {
                if (!token.IsCancellationRequested)
                {
                    curImage++;
                    mainThreadPump.WaitText = $"Adding to the project ({curImage} of {totalImages}):\r\n{image.Path}";
                    this.GenerateImage(image);
                    this.OnGeneratedImage(image);
                }
            }
        }