/// <summary> /// Constructor of the FrameworkElement representation, which can be moved anywhere on the screen /// </summary> /// <param name="element">Element to create an avatar from</param> /// <param name="scale">Scale of the represented avatar compared to its source (default: 1.0: equal size)</param> /// <param name="opacity">Opacity of the represented avatar compared to its source (default: 1.0)</param> public FreeFrameworkElementAvatar(FrameworkElement element, double scale = 1.0, double opacity = 1.0) { if (element.GetType() == typeof(ContentPresenter)) { element = WPF.FindFirstChild <FrameworkElement>(element); } // Create the image Image = new Image() { // Icon Image Style = new Style(typeof(Image)), Source = WPF.ProduceImageSourceForVisual(element), Opacity = opacity, }; // Create the visual DragIcon = new Popup() { AllowsTransparency = true, PopupAnimation = PopupAnimation.None, Placement = PlacementMode.Absolute, Width = element.ActualWidth * scale, Height = element.ActualHeight * scale, Child = Image }; }
/// <summary> /// Bring the avatar at a given screen position /// </summary> /// <param name="position">screen position</param> public void GotoPosition(Point position) { if (DragIcon != null) { if (!DragIcon.IsOpen) { DragIcon.IsOpen = true; // Make the icon transparent to the mouse. Unfortunatly, this code // is sytem dependent. Alternative is to change the following icon position // to not be under the the mouse (remove the offset for example). WPF.SetWindowExTransparent(DragIcon.Child); } // Icon position with respect to mouse position. // Remove the Y offset if SetWindowExTransparent is not available on the system. DragIcon.HorizontalOffset = position.X - DragIcon.Width / 2.0; DragIcon.VerticalOffset = position.Y - DragIcon.Height / 2.0; } }
// ---------------------------------------------------------------------------------------------- #region Class functions private static void TargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var wxthis = (ItemsControl)d; if (e.OldValue is IItemsRangeInfo rinfo) { rinfo.Close(); } var enable = e.NewValue is IItemsRangeInfo || e.NewValue is ISelectionInfo; if (enable) { // Lazy declaration of Timer if (Timer == null) { Timer = new DispatcherTimer(); Timer.Tick += OnTimerElapsed; } wxthis.Loaded += OnLoaded; wxthis.Unloaded += OnUnloaded; } else { wxthis.Loaded -= OnLoaded; wxthis.Unloaded -= OnUnloaded; } ScrollViewer wxscroll; if (wxthis is ListBox) { wxscroll = WPF.FindFirstChild <ScrollViewer>(wxthis); } else { wxscroll = WPF.FindFirstParent <ScrollViewer>(wxthis); } if (References == null) { References = new Dictionary <ScrollViewer, List <WeakReference> >(); } if (References.TryGetValue(wxscroll, out var reflist)) { // Remove dead references for (int i = 0; i < reflist.Count; i++) { var wref = reflist[i]; if (!wref.IsAlive || wref.Target == wxthis) { reflist.RemoveAt(i); i--; } } } else if (enable) { reflist = new List <WeakReference>(); References.Add(wxscroll, reflist); } if (enable) { reflist.Add(new WeakReference(wxthis)); if (reflist.Count == 1) { wxscroll.ScrollChanged += OnScrollChanged; } } else if (reflist == null || reflist.Count == 0) { wxscroll.ScrollChanged -= OnScrollChanged; } }
/// <summary> /// Update the virtualization of the scrollviewer in <see cref="Timer.Tag"/> /// </summary> /// <param name="sender"><see cref="Timer"/></param> /// <param name="e"></param> private static void OnTimerElapsed(object sender = null, EventArgs e = null) { LimeMsg.Debug("DataVirtualization OnTimerElapsed"); Timer.Stop(); var wxscroll = (ScrollViewer)Timer.Tag; Timer.Tag = null; var reflist = References[wxscroll]; // TODO: optimize to take into account what was visible previously to not loop all over again foreach (var wref in reflist) { if (!wref.IsAlive || !(wref.Target is ItemsControl wxctrl)) { continue; } var itemgen = wxctrl.ItemContainerGenerator; var itemcnt = itemgen.Items.Count; int startSelected = -1; int endSelected = -1; var rangeSelected = new List <ItemIndexRange>(); int startVisible = -1; int endVisible = -1; var rangeVisible = new List <ItemIndexRange>(); for (int i = 0; i < itemcnt; i++) { if (!(itemgen.ContainerFromIndex(i) is FrameworkElement wxitem)) { continue; } if (wxitem.DataContext is IItemsRangeInfo) { // Detect selected items if (wxitem.IsFocused) { if (startSelected == -1) { startSelected = i; } endSelected = i; } else if (Selector.GetIsSelected(wxitem)) { if (startSelected == -1) { startSelected = i; } endSelected = i; } else if (startSelected != -1) { rangeSelected.Add(new ItemIndexRange(startSelected, (uint)(endSelected - startSelected + 1))); startSelected = -1; } // Detect visible items if (WPF.IsFullyOrPartiallyVisible(wxitem, wxscroll)) { if (startVisible == -1) { startVisible = i; } endVisible = i; } else if (startVisible != -1) { rangeVisible.Add(new ItemIndexRange(startVisible, (uint)(endVisible - startVisible + 1))); startVisible = -1; } } } var target = GetTarget(wxctrl); // TODO: handle selection if (target is IItemsRangeInfo rinfo) { if (startVisible != -1) { rangeVisible.Add(new ItemIndexRange(startVisible, (uint)(endVisible - startVisible + 1))); startVisible = -1; } if (rangeVisible.Count > 0) { rinfo.RangesChanged(rangeVisible[0], new ReadOnlyCollection <ItemIndexRange>(rangeVisible)); } } } }
// ---------------------------------------------------------------------------------------------- #region Impelmentation /// <summary> /// Create or handle a system-style Drag & Drop icon /// </summary> /// <param name="element">Element to represent or where the Drag & Drop operation occurs</param> /// <param name="dataObject">Data Object dragged during Drag & Drop</param> /// <param name="effects">Drag & Drop effects</param> /// <param name="scale">Scale of the element representation when creating icon (1.0: unchanged)</param> /// <param name="create">create the icon representation from the element, otherwise tries to continue the existing Drag & Drop from the dataObject</param> public DragSystemIcon(FrameworkElement element, IDataObject dataObject, DragDropEffects effects, double scale, bool create) { // retrieve window handle var window = PresentationSource.FromVisual(element).RootVisual; var hwnd = ((HwndSource)PresentationSource.FromVisual(window)).Handle; if (create) { Element = element; if (element is ContentPresenter) { element = WPF.FindFirstChild <FrameworkElement>(element); } // Convert the visual element to a Bitmap var dpi = 96 * scale; var renderbmp = WPF.ProduceImageSourceForVisual(element, dpi, dpi); // Retrieve renderbmp pixel array const int pixsize = 4; // Pbgra32 implies 4 bytes per pixels int stride = renderbmp.PixelWidth * pixsize; byte[] pix = new byte[renderbmp.PixelHeight * stride]; renderbmp.CopyPixels(pix, stride, 0); for (int i = 3; i < pix.Length; i += pixsize) { if (pix[i] == 0) { // Convert fully transparent pixels to Magenta (this will become transparent in ShDragImage) pix[i - 0] = 0xFF; // A pix[i - 1] = 0xFF; // R pix[i - 2] = 0x00; // G pix[i - 3] = 0xFF; // B } else if (pix[i] == 0xFF && pix[i - 1] == 0xFF && pix[i - 2] == 0x00 && pix[i - 3] == 0xFF) { // change Magenta pixels to *almost* magenta, to avoid changing these to transparent in ShDragImage pix[i - 2] = 0x01; } } // Convert pixel array to BitmapSource var bitmapsrc = BitmapSource.Create(renderbmp.PixelWidth, renderbmp.PixelHeight, 96, 96, PixelFormats.Bgra32, null, pix, stride); // Convert BitmapSource to Bitmap System.Drawing.Bitmap bitmap; using (System.IO.MemoryStream stream = new System.IO.MemoryStream()) { var encoder = new BmpBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bitmapsrc)); encoder.Save(stream); bitmap = new System.Drawing.Bitmap(stream); } //LimeMsg.Debug("ClipDragDrop DragSystemIcon: {0}", bitmap.GetPixel(0, 0)); // Compute destination size WPF.Win32Size size; size.cx = (int)(renderbmp.PixelWidth * scale); size.cy = (int)(renderbmp.PixelHeight * scale); WPF.SysPoint wpt; wpt.x = size.cx / 2; wpt.y = size.cy / 2; ShDragImage shdi = new ShDragImage { sizeDragImage = size, ptOffset = wpt, hbmpDragImage = bitmap.GetHbitmap(), crColorKey = System.Drawing.Color.Magenta.ToArgb() }; var sourceHelper = (IDragSourceHelper2) new DragDropHelper(); sourceHelper.SetFlags(1); // Enable Drop description // Not quite right var com = new ComDataObject(); dataObject = new DataObject(com); sourceHelper.InitializeFromBitmap(ref shdi, (System.Runtime.InteropServices.ComTypes.IDataObject)dataObject); } else { Element = null; } ComData = dataObject; Effects = effects; Scale = scale; // Create the System Drag Helper and show it at the mouse location WPF.GetCursorPos(out WPF.SysPoint spos); DDHelper = (IDropTargetHelper) new DragDropHelper(); DDHelper.DragEnter(hwnd, (System.Runtime.InteropServices.ComTypes.IDataObject)dataObject, ref spos, Effects); }