[英]FindParent in WPF CustomControl return null
我正在嘗試制作一個圓形滑塊(一個自定義控件)以選擇一種顏色的色相。 我在運行演示應用程序時遇到了這個問題。 在這種情況下,FindParent(“ _ templateCanvas”)始終為null,但找不到原因。
這是HueWheel類的一部分:
public class HueWheel : Slider
{
static HueWheel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HueWheel), new FrameworkPropertyMetadata(typeof(HueWheel)));
}
private bool _isPressed = false;
private Canvas _templateCanvas = null;
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
_isPressed = true;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
_isPressed = false;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_isPressed)
{
if (_templateCanvas == null)
{
_templateCanvas = MyHelper.FindParent<Canvas>(e.Source as Ellipse);
if (_templateCanvas == null) return;
}
const double RADIUS = 150;
Point newPos = e.GetPosition(_templateCanvas);
double angle = MyHelper.GetAngleR(newPos, RADIUS);
//huewheel.Value = (huewheel.Maximum - huewheel.Minimum) * angle / (2 * Math.PI);
}
}
}
這是找到父級的類:
public static class MyHelper
{
public static T FindParent<T>(FrameworkElement current) where T : FrameworkElement
{
do
{
current = VisualTreeHelper.GetParent(current) as FrameworkElement;
if (current is T)
{
return (T)current;
}
}
while (current != null);
return null;
}
這是我在Generic.xaml中擁有的
<Style TargetType="{x:Type local:HueWheel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:HueWheel}">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Slider Name="huewheel">
<Slider.Template>
<ControlTemplate>
<Viewbox>
<Canvas Width="300" Height="300">
<Image Stretch="Fill" Source="Assets/HueCircle.PNG" Focusable="False" Height="300" Width="300" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="270"/>
<TranslateTransform/>
</TransformGroup>
</Image.RenderTransform>
</Image>
<Ellipse Fill="Transparent" Width="300" Height="300" Canvas.Left="0" Canvas.Top="0"/>
<Canvas>
<Line Stroke="Transparent" StrokeThickness="5" X1="150" Y1="150" X2="150" Y2="0"/>
<Ellipse Fill="Black" Width="20" Height="20" Canvas.Left="140" Canvas.Top="30"/>
<Canvas.RenderTransform>
<RotateTransform CenterX="150" CenterY="150">
<RotateTransform.Angle>
<MultiBinding Converter="{StaticResource ValueAngleConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Minimum"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Maximum"/>
</MultiBinding>
</RotateTransform.Angle>
</RotateTransform>
</Canvas.RenderTransform>
</Canvas>
</Canvas>
</Viewbox>
</ControlTemplate>
</Slider.Template>
</Slider>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
畫布存在於xaml中,我在其上移動鼠標的橢圓包含在此畫布中,因此我應該能夠獲取橢圓父對象(畫布)。
任何想法 ?
謝謝
或者,“畫布有什么關系?”
我嘗試測試您的代碼,發現e.Source
是HueWheel
類型,這並不奇怪,該類型繼承自Slider
而不是Ellipse
。 因此,此表達式始終返回null
:
e.Source as Ellipse
...因此,a) e.Source
不是您認為的對象,b)下_templateCanvas
語句始終將_templateCanvas
設置為null
:
_templateCanvas = MyHelper.FindParent<Canvas>(e.Source as Ellipse);
FindParent
立即返回null
,因為您始終向其傳遞current
參數的null
。 無論如何,這兩個畫布都不是HueWheel
的父 HueWheel
(您知道-您不知道的是e.Source
是 HueWheel
但下次您要做的第一件事就是拍斷點並懸停將鼠標懸停在e.Source
以查看所獲得的內容)。
現在,讓我們對其進行修復。 接下來是如何在WPF中做到這一點,除了真正聰明的人在評論中困擾我的細節之外。
我不知道您要在此處嘗試使用哪個Canvas,所以我會同時將它們都抓住。 首先,在模板中給它們x:Name
屬性; 我將它們PART_FirstCanvas
和PART_SecondCanvas
因為我沒有弄清楚語義。 我還重寫了模板,以消除內部滑塊; 我不明白為什么要將重新模板化的滑塊放在滑塊的模板中。 那里的問題是我無法調用內部滑塊的GetTemplateChild()
方法,因為它是受保護的。 我的另一個選擇是編寫Slider
的最小子類,該子類僅公開公開GetTemplateChild()
,並將其用於內部滑塊。 如果有充分的理由要擁有兩個對我來說不明顯的滑塊(也許一個是X坐標,另一個是Y?),我們可以更新它以這種方式進行。
<ControlTemplate
TargetType="{x:Type test:HueWheel}"
>
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
>
<Viewbox>
<Canvas
Width="300"
Height="300"
x:Name="PART_FirstCanvas"
>
<Image
Stretch="Fill"
Source="Assets/HueCircle.PNG"
Focusable="False"
Height="300"
Width="300"
RenderTransformOrigin="0.5,0.5"
>
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="270"/>
<TranslateTransform/>
</TransformGroup>
</Image.RenderTransform>
</Image>
<Ellipse
Fill="Transparent"
Width="300"
Height="300"
Canvas.Left="0"
Canvas.Top="0"/>
<Canvas
x:Name="PART_SecondCanvas">
<Line Stroke="Transparent" StrokeThickness="5" X1="150" Y1="150" X2="150" Y2="0"/>
<Ellipse Fill="Black" Width="20" Height="20" Canvas.Left="140" Canvas.Top="30"/>
<Canvas.RenderTransform>
<RotateTransform CenterX="150" CenterY="150">
<RotateTransform.Angle>
<MultiBinding Converter="{StaticResource ValueAngleConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Minimum"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Maximum"/>
</MultiBinding>
</RotateTransform.Angle>
</RotateTransform>
</Canvas.RenderTransform>
</Canvas>
</Canvas>
</Viewbox>
</Border>
</ControlTemplate>
接下來,我們將為這兩個名為Canvas的成員添加私有成員,並在OnApplyTemplate()
的重寫中獲取它們。 然后,我們將在該mousemove事件中使用任意一個:
public class HueWheel : Slider
{
static HueWheel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HueWheel), new FrameworkPropertyMetadata(typeof(HueWheel)));
}
private bool _isPressed = false;
private Canvas _PART_FirstCanvas;
private Canvas _PART_SecondCanvas;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Use () cast rather than the "as" operator because if the actual runtime
// type can't be cast to the desired type, that'll throw an exception
// rather than silently returning null. If you had cast (Ellipse)e.Source,
// that would have blown up on you because you can't cast HueWheel to Ellipse.
// That would have instantly shown you what was wrong.
// But you won't get an exception here if GetTemplateChild() just returns null.
_PART_FirstCanvas = (Canvas)GetTemplateChild("PART_FirstCanvas");
_PART_SecondCanvas = (Canvas)GetTemplateChild("PART_SecondCanvas");
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_isPressed)
{
const double RADIUS = 150;
// Or _PART_SecondCanvas; whichever.
Point newPos = e.GetPosition(_PART_FirstCanvas);
double angle = MyHelper.GetAngleR(newPos, RADIUS);
//huewheel.Value = (huewheel.Maximum - huewheel.Minimum) * angle / (2 * Math.PI);
}
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
_isPressed = true;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
_isPressed = false;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.