簡體   English   中英

返回null時如何遞歸選擇AdornedElement的父級

[英]How to recursively select the parent of AdornedElement when returning null

好的,所以我之前收到的答案在這里 ,效果非常好。 然而,在已知問題中,它提到了以下內容:

此外,這不會立即在UserControls內部工作,因為AdornerLayer.GetAdornerLayer(AdornedElement)將在UserControls內返回null 這可以通過查找UserControl的父級的AdornerLayer (或父級的父級,遞歸)來輕松修復。 有這樣的功能。

因此,我在大多數情況下都使用了代碼,但是當我嘗試在tabcontrol中的元素上使用它時遇到了問題。而不是具有所需的效果,模糊效果僅適用於TabItem,而不是整個窗口。 此外,tabItem的內容似乎打印出幾次作為各種視覺畫筆。 這是一個例子。 Custom Decorator包含一個包含2個文本塊的stackpanel,一個包含“GP:”,另一個包含一個數字。 以下是在標簽項中應用此內容時的前后照片:

之前

后

那么,我該如何糾正這個?

我會在這里發布我的代碼片段,因為我已經稍微修改了它們的答案(雖然不是以“破壞”這個方式)

這是XAML,帶有裝飾器:

<models:TipFocusDecorator IsOpen="{Binding TutorialBoolGP}" TipHead="{Binding TutorialTitle}" TipText="{Binding TutorialDescription}" TipPos="{Binding TutorialPosition}">
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Background="Transparent">
        <TextBlock Text="GP: " />
        <TextBlock Text="{Binding PlayerGP, Converter={StaticResource IntToComma}}" />
    </StackPanel>
</models:TipFocusDecorator>

裝飾者:

public class TipFocusDecorator : Decorator
{

    public bool IsOpen
    {
        get { return (bool)GetValue(IsOpenProperty); }
        set { SetValue(IsOpenProperty, value); }
    }
    // Using a DependencyProperty as the backing store for Open.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsOpenProperty =
        DependencyProperty.Register("IsOpen", typeof(bool), typeof(TipFocusDecorator),
        new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsOpenPropertyChanged));


    public string TipText
    {
        get { return (string)GetValue(TipTextProperty); }
        set { SetValue(TipTextProperty, value); }
    }
    // Using a DependencyProperty as the backing store for TipText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TipTextProperty =
        DependencyProperty.Register("TipText", typeof(string), typeof(TipFocusDecorator), new UIPropertyMetadata(string.Empty));


    public string TipHead
    {
        get { return (string)GetValue(TipHeadProperty); }
        set { SetValue(TipHeadProperty, value); }
    }
    // Using a DependencyProperty as the backing store for TipText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TipHeadProperty =
        DependencyProperty.Register("TipHead", typeof(string), typeof(TipFocusDecorator), new UIPropertyMetadata(string.Empty));


    public string TipPos
    {
        get { return (string)GetValue(TipPosProperty); }
        set { SetValue(TipPosProperty, value); }
    }
    // Using a DependencyProperty as the backing store for TipPos.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TipPosProperty =
        DependencyProperty.Register("TipPos", typeof(string), typeof(TipFocusDecorator), new UIPropertyMetadata(string.Empty));


    public bool HasBeenShown
    {
        get { return (bool)GetValue(HasBeenShownProperty); }
        set { SetValue(HasBeenShownProperty, value); }
    }

    // Using a DependencyProperty as the backing store for HasBeenShown.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HasBeenShownProperty =
        DependencyProperty.Register("HasBeenShown", typeof(bool), typeof(TipFocusDecorator), new UIPropertyMetadata(false));

    private static void IsOpenPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var decorator = sender as TipFocusDecorator;

        if ((bool)e.NewValue)
        {
            if (!decorator.HasBeenShown)
                decorator.HasBeenShown = true;

            decorator.Open();
        }

        if (!(bool)e.NewValue)
        {
            decorator.Close();
        }
    }

    TipFocusAdorner adorner;

    protected void Open()
    {
        adorner = new TipFocusAdorner(this.Child);
        var adornerLayer = AdornerLayer.GetAdornerLayer(this.Child);
        adornerLayer.Add(adorner);
        TutorialTip tip = new TutorialTip(TipHead,TipText,TipPos);
        tip.Owner = Application.Current.MainWindow;
        double width = tip.Width;
        double height = tip.Height;
        Point position = this.Child.PointToScreen(new Point(0d, 0d));
        switch (TipPos)
        {
            case "Bottom":
                position.X += (this.Child.RenderSize.Width / 2) - (width / 2);
                position.Y += this.Child.RenderSize.Height + 10;
                break;
            case "Top":
                position.X += (this.Child.RenderSize.Width / 2) - (width / 2);
                position.Y += -height - 10;
                break;
            case "Left":
                position.X += -width - 10;
                position.Y += (this.Child.RenderSize.Height / 2) - (height / 2);
                break;
            case "Right":
                position.X += this.Child.RenderSize.Width + 10;
                position.Y += (this.Child.RenderSize.Height / 2) - (height / 2);
                break;
        }
        tip.Left = position.X;
        tip.Top = position.Y;
        tip.ShowDialog();
        //MessageBox.Show(TipText + position);  // Change for your custom tip Window
        IsOpen = false;
    }

    protected void Close()
    {
        var adornerLayer = AdornerLayer.GetAdornerLayer(this.Child);
        adornerLayer.Remove(adorner);
        adorner = null;
    }

}

最后是Adorner:

public class TipFocusAdorner : Adorner
{
    public TipFocusAdorner(UIElement adornedElement)
        : base(adornedElement)
    {
    }

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        var root = Window.GetWindow(this);
        var adornerLayer = AdornerLayer.GetAdornerLayer(AdornedElement);
        var presentationSource = PresentationSource.FromVisual(adornerLayer);
        Matrix transformToDevice = presentationSource.CompositionTarget.TransformToDevice;

        var sizeInPixels = transformToDevice.Transform((Vector)adornerLayer.RenderSize);
        RenderTargetBitmap rtb = new RenderTargetBitmap((int)(sizeInPixels.X), (int)(sizeInPixels.Y), 96, 96, PixelFormats.Default);

        var oldEffect = root.Effect;
        var oldVisibility = AdornedElement.Visibility;
        root.Effect = new BlurEffect();
        AdornedElement.SetCurrentValue(FrameworkElement.VisibilityProperty, Visibility.Hidden);
        rtb.Render(root);
        AdornedElement.SetCurrentValue(FrameworkElement.VisibilityProperty, oldVisibility);
        root.Effect = oldEffect;

        drawingContext.DrawImage(rtb, adornerLayer.TransformToVisual(AdornedElement).TransformBounds(new Rect(adornerLayer.RenderSize)));
        drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(22, 0, 0, 0)), null, adornerLayer.TransformToVisual(AdornedElement).TransformBounds(new Rect(adornerLayer.RenderSize)));
        drawingContext.DrawRectangle(new VisualBrush(AdornedElement) { AlignmentX = AlignmentX.Left, TileMode = TileMode.None, Stretch = Stretch.None },
            null,
            AdornedElement.RenderTransform.TransformBounds(new Rect(AdornedElement.RenderSize)));
    }
}

AdornerLayer.GetAdornerLayer檢索給定元素上方(父級)的裝飾層。 因此,在UserControl中返回null並不一定正確。 只要在UserControl上方存在AdornerLayer,它就會返回。 默認情況下,窗口會創建一個AdornerLayer,但只有在加載后才會創建。 事實上,我使用簡單的方法測試了您的代碼

<Grid x:Name="Container">
    <DockPanel>
        <TextBlock DockPanel.Dock="Top">Outside of Tab</TextBlock>
        <TabControl x:Name="TabControl">
            <TabItem Header="Here">
                <local:UserControlContainingTipFocus/>
            </TabItem>
        </TabControl>
    </DockPanel>
</Grid>

我無法重現這個問題。 模糊應用於窗口內的所有內容

因此,在您的情況下,必須有UserControl的父級創建AdornerLayer。 我猜測TabControl或TabItem。 您可以使用Snoop進行檢查。

但不用擔心,您可以創建自己的AdornerLayer。 只需將要模糊的元素放在AdornerDecorator中即可。

<Window >
    <AdornerDecorator>
        <Grid x:Name="Container">
            <DockPanel>
                <TextBlock DockPanel.Dock="Top">Outside of adorner</TextBlock>
                    <TabControl x:Name="TabControl">
                        <TabItem Header="Here">
                            <local:TestControl></local:TestControl>
                        </TabItem>
                    </TabControl>
            </DockPanel>
        </Grid>
    </AdornerDecorator>
</Window>

然后修改對AdornerLayer.GetAdornerLayer的每次調用。 而不是傳遞原始元素,傳遞您想要模糊的容器。 在我的例子中,它是Grid或AdornerDecorator本身。

var root = Window.GetWindow(this);
var blurContainer = (Visual) root.Content;
var adornerLayer = AdornerLayer.GetAdornerLayer(blurContainer);

上面的代碼使用Window.GetWindow並訪問它的內容(第一個孩子)。 但您可以在TipFocusAdorner / Decorator中輕松創建屬性,以指定要傳遞給AdornerLayer.GetAdornerLayer的元素

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM