0

I am attempting to "blurr" a windows 10 user control when an adorner is placed over it.

More exactly, I have a datagrid control in a wpf user window. When a cell in the datagrid is selected, an editing adorner is created over the datagrid control.

public DataGridAnnotationAdorner(DataGrid adornedDataGrid, IVisit visit, DateTime TableDate)
            : base(adornedDataGrid)
        {
            Control = new DataGridAnnotationControl(visit, TableDate);

            AddLogicalChild(Control);
            AddVisualChild(Control);

            Loaded += DataGridAnnotationAdorner_Loaded;    
        }

        private void DataGridAnnotationAdorner_Loaded(object sender, RoutedEventArgs e)
        {
            EnableBlur(AdornedElement);
        }

The loaded event then calls (a pretty standard) Blurr/glass effect method:

   internal void EnableBlur(UIElement dataGrid)
    {
        HwndSource source = (HwndSource)PresentationSource.FromVisual(dataGrid);
        IntPtr hwnd = source.Handle;

        var accent = new AccentPolicy();
        accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;

        var accentStructSize = Marshal.SizeOf(accent);

        var accentPtr = Marshal.AllocHGlobal(accentStructSize);
        Marshal.StructureToPtr(accent, accentPtr, false);

        var data = new WindowCompositionAttributeData();
        data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
        data.SizeOfData = accentStructSize;
        data.Data = accentPtr;

        SetWindowCompositionAttribute(hwnd, ref data);

        Marshal.FreeHGlobal(accentPtr);
    }

No error is reported....But is does not work.

So, how can I give a "glass" appearance to the control under the adorner?

TIA

Alan Wayne
  • 5,122
  • 10
  • 52
  • 95
  • Hope you don't mind my posting re the question you just deleted but I was about to comment this in case it helps: Possible duplicate of [Delete parent if it's not referenced by any other child](https://stackoverflow.com/a/15810159/3404097) PS I don't know whether the locking solution is based on guaranteed behaviour or the answerer is just unjustifiably assuming an implementation. – philipxy Apr 23 '20 at 02:36

1 Answers1

1

I'm not sure I can give you a whole solution, but I can at least explain why your attempt doesn't work and maybe point you in the right direction.

In WinForms (and C++ I think), each control is sort of its own "mini-window". By which I mean each control is given its own "handle" by the OS, and the OS treats them as individual entities on the screen. WPF is different, the illusion of "separate controls" in a window is one created by the WPF framework. As far as the OS knows, there is only one entity: the Window and all further logic is handled internally by WPF. This is only mostly true, though, as I'll bring up later.

The reason I bring this up at all is because your line of code PresentationSource.FromVisual(dataGrid) is going to give you the handle of the Window in which the DataGrid is being hosted, not a handle unique to the DataGrid itself (since the DataGrid has no handle). And so everything else you do using that handle is actually being targeted to the entire WPF Window as a whole.

All of the rendering inside a WPF Window is handled internally by the WPF framework (plus your code), not directly by the OS (as in WinForms and C++), so using a native method that tells the OS how to render something wont work on a specific element inside the Window.

As I see it there are two routes you can take in trying to create this effect:

  • You could try to recreate the "glassy" effect inside WPF. This would involve recreating or getting ahold of whatever algorithm is used to visually distort an area to produce that effect, and then somehow implementing that into a control. I wouldn't really know where to start with that other than just Google.

  • You could try to take advantage of other WPF elements that do have their own handles.

    • The two main WPF elements I know of that have their own handles are Window and Popup. You might be able to create one of those and position it where you need it, just like you're currently doing with your adorer. This will get tricky though because these elements are actually outside the Window that spawned them and aren't really linked to it. So when you move the main Window across the desktop, the Popup or sub-Window won't move with it. You might also be able to drag other windows between your WPF window and the popup you create (not sure about that, though).

    • There is maybe one other possibility under this heading, and that is the WindowsFormsHost control. This is a WPF control that hosts a WinForms control in a WPF window. The WinForms controls hosted inside presumably are given their own handle by the OS (since that's how WinForms operates). This one might be your best bet, except I've never tried it and I have no idea how the WindowsFormsHost will interact with other elements that are around or underneath it. So it also might not work at all for what you need.

Good luck.

Keith Stein
  • 6,235
  • 4
  • 17
  • 36
  • Thanks. I did not know that the operating system was responsible for the blur effect. I was hoping I could still blur the window, but I did not get why the Adorner was not being blurred -- as it is part of the window. I'm guessing that WPF does its own rendering, and does not respect the blurring effect. Too bad :( Or, since I am doing the blur BEFORE rendering the adorner, maybe the adorner is undoing the blur? Oh well, it was worth the try. – Alan Wayne Mar 29 '20 at 10:13
  • Yes, the `Adorner` is actually just another part of the `Window`, just placed on top of everything else. It's still rendered internally. – Keith Stein Mar 29 '20 at 16:18