好的,我之前收到的答案Here,效果非常好。然而,在已知问题中,它提到了以下内容:
此外,这不会立即在UserControls内部工作,因为
AdornerLayer.GetAdornerLayer(AdornedElement)
将返回null
在UserControls中。通过寻找可以很容易地解决这个问题AdornerLayer
(或父母的父母)的父母UserControl
父母,递归地)。有功能可以这样做。
所以,我在大多数情况下都使用了代码,但是当我尝试在tabcontrol中的元素上使用它时遇到了问题。而不是具有所需的效果,模糊效果仅适用于TabItem,而不是整个窗口。此外,tabItem的内容似乎打印出几次作为各种视觉画笔。这是一个例子。 Custom Decorator包含一个包含2个文本块的stackpanel,其中一个包含" GP:"另一个包含一个数字。以下是在标签项中应用此内容时的前后镜头:
那么,我该如何纠正这个?
我会在这里发布我的代码片段,因为我已经稍微修改了它们的答案(虽然没有以及#34;破坏"这个)
这是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)));
}
}
答案 0 :(得分:5)
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的元素