ContentTemplate内の要素を取得したい
解決したいこと
UserControlの中でContentTemplateを定義しています。そのContentTemplateで定義している要素(NameがWidthCellのDockPanel)をプロパティの中からアクセスして取得したいのですが、FindName(string)を実行すると、nullが返ってきます...。また巷で流布されているFindVisualChildren()を使ってみても、目的の要素は取得できません。どうしたら取得できるでしょうか?
DetailPie -(use)-> DetailPathGeometry -(include)-> DockPanel(WidthCell)という関係です。
using boilersGraphics.Controls;
using boilersGraphics.Extensions;
using boilersGraphics.ViewModels;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;
using static boilersGraphics.Views.DetailPathGeometry;
namespace boilersGraphics.Views
{
public class DetailPie : UserControl
{
private DetailPathGeometry _detailPathGeometry;
public DetailPie()
{
Name = "DetailPie";
Loaded += DetailPie_Loaded;
}
private void DetailPie_Loaded(object sender, RoutedEventArgs e)
{
var dockPanel = new DockPanel();
var childDockPanel = new DockPanel();
childDockPanel.SetValue(DockPanel.DockProperty, Dock.Bottom);
var grandchildDockPanel = new DockPanel();
grandchildDockPanel.SetValue(DockPanel.DockProperty, Dock.Right);
var button = new Button();
button.Width = 100;
button.Margin = new System.Windows.Thickness(5);
button.HorizontalAlignment = HorizontalAlignment.Right;
button.SetBinding(ButtonBase.CommandProperty, "OKCommand");
button.Content = "OK";
grandchildDockPanel.Children.Add(button);
childDockPanel.Children.Add(grandchildDockPanel);
dockPanel.Children.Add(childDockPanel);
_detailPathGeometry = new DetailPathGeometry();
_detailPathGeometry.Name = "DetailPathGeometry";
_detailPathGeometry.SetBinding(FrameworkElement.DataContextProperty, "ViewModel.Value");
_detailPathGeometry.CenterVisibility = Visibility.Collapsed;
_detailPathGeometry.Stretch = System.Windows.Media.Stretch.Fill;
_detailPathGeometry.SetBinding(WidthPlacementProperty, new Binding("WidthPlacement") { Source = this });
var canvas = new Canvas();
var centerPoint = new Ellipse();
centerPoint.Width = 5;
centerPoint.Height = 5;
centerPoint.Fill = Brushes.Red;
var viewModel = ((DataContext as DetailPieViewModel).ViewModel.Value as NPieViewModel);
centerPoint.SetValue(Canvas.LeftProperty, viewModel.PieCenterPoint.Value.X - viewModel.Left.Value + 100);
centerPoint.SetValue(Canvas.TopProperty, viewModel.PieCenterPoint.Value.Y - viewModel.Top.Value + 100);
canvas.Children.Add(centerPoint);
var stackPanel = new StackPanel();
stackPanel.Orientation = Orientation.Horizontal;
stackPanel.SetValue(Canvas.LeftProperty, viewModel.PieCenterPoint.Value.X - viewModel.Left.Value + 115);
stackPanel.SetValue(Canvas.TopProperty, viewModel.PieCenterPoint.Value.Y - viewModel.Top.Value + 115);
var style = new Style();
style.TargetType = typeof(DoubleTextBox);
var setter = new Setter();
setter.Property = WidthProperty;
setter.Value = 40;
style.Setters.Add(setter);
stackPanel.Resources.Add(string.Empty, style);
var label = new Label();
label.Foreground = Brushes.Red;
label.Content = "Center Point";
stackPanel.Children.Add(label);
var doubleTextBox = new DoubleTextBox();
doubleTextBox.Foreground = Brushes.Red;
doubleTextBox.SetBinding(DoubleTextBox.DoubleTextProperty, "PieCenterPoint.Value.X");
stackPanel.Children.Add(doubleTextBox);
var doubleTextBox2 = new DoubleTextBox();
doubleTextBox2.Foreground = Brushes.Red;
doubleTextBox2.SetBinding(DoubleTextBox.DoubleTextProperty, "PieCenterPoint.Value.Y");
stackPanel.Children.Add(doubleTextBox2);
canvas.Children.Add(stackPanel);
_detailPathGeometry.Content = canvas;
dockPanel.Children.Add(_detailPathGeometry);
Content = dockPanel;
}
public Placement WidthPlacement
{
get
{
var detailPathGeometry = _detailPathGeometry;
var children = this.FindVisualChildren<DependencyObject>().ToList();
var target = children.FirstOrDefault(x => x is FrameworkElement xx && xx.Name == "WidthCell");
// target is null!!!
var obj = detailPathGeometry.FindName("WidthCell");
// obj is null!!!
// コードが続きます...
return Placement.Top; //仮設定
}
}
}
}
<UserControl x:Class="boilersGraphics.Views.DetailPathGeometry"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:control="clr-namespace:boilersGraphics.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
x:Name="userControl"
mc:Ignorable="d">
<UserControl.ContentTemplate>
<DataTemplate>
<Grid>
<Grid>
<Grid.Resources>
<Style TargetType="control:DoubleTextBox">
<Setter Property="Width" Value="40" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="{Binding DataContext.Width.Value, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="1" />
<RowDefinition Height="{Binding DataContext.Height.Value, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
<RowDefinition Height="1" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Line Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="3"
Margin="-20"
Stretch="Fill"
Stroke="Red"
StrokeDashArray="2 2"
X2="1" />
<Line Grid.Row="3"
Grid.Column="1"
Grid.ColumnSpan="3"
Margin="-20"
Stretch="Fill"
Stroke="Red"
StrokeDashArray="2 2"
X2="1" />
<Line Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="1"
Margin="-20"
Stretch="Fill"
Stroke="Red"
StrokeDashArray="2 2"
Y2="1" />
<Line Grid.Row="1"
Grid.RowSpan="3"
Grid.Column="3"
Margin="-20"
Stretch="Fill"
Stroke="Red"
StrokeDashArray="2 2"
Y2="1" />
<Path Grid.Row="2"
Grid.Column="2"
Data="{Binding DataContext.PathGeometry.Value, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
Fill="{Binding DataContext.FillColor.Value, Converter={StaticResource solidColorBrushConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
IsHitTestVisible="False"
Stretch="{Binding Stretch, ElementName=userControl}"
Stroke="{Binding DataContext.EdgeColor.Value, Converter={StaticResource solidColorBrushConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
StrokeThickness="{Binding DataContext.EdgeThickness.Value, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
<Ellipse Grid.Row="2"
Grid.Column="2"
Width="5"
Height="5"
Fill="Red"
Visibility="{Binding CenterVisibility, ElementName=userControl}" />
</Grid>
<Grid>
<Grid.Resources>
<Style TargetType="control:DoubleTextBox">
<Setter Property="Width" Value="40" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="85" />
<ColumnDefinition Width="31" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="31" />
<ColumnDefinition Width="85" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="85" />
<RowDefinition Height="31" />
<RowDefinition Height="*" />
<RowDefinition Height="31" />
<RowDefinition Height="85" />
</Grid.RowDefinitions>
<DockPanel Grid.Row="1" Grid.Column="0">
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Label Content="Top" />
<control:DoubleTextBox VerticalContentAlignment="Center" DoubleText="{Binding DataContext.Top.Value, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
</StackPanel>
</DockPanel>
<DockPanel Grid.Row="0"
Grid.Column="1"
Grid.ColumnSpan="2">
<StackPanel HorizontalAlignment="Left"
VerticalAlignment="Center"
Orientation="Horizontal">
<Label Content="Left" />
<control:DoubleTextBox VerticalContentAlignment="Center" DoubleText="{Binding DataContext.Left.Value, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
</StackPanel>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Label Content="Rotate" />
<control:DoubleTextBox VerticalContentAlignment="Center" DoubleText="{Binding DataContext.RotationAngle.Value, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
<Label Content="°" />
</StackPanel>
</DockPanel>
<DockPanel Name="WidthCell"
Grid.Row="{c:Binding WidthRow, ElementName=userControl}"
Grid.Column="2">
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Top"
Orientation="Horizontal">
<Label Content="Width" />
<control:DoubleTextBox VerticalContentAlignment="Center" DoubleText="{Binding DataContext.Width.Value, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
</StackPanel>
</DockPanel>
<DockPanel Grid.Row="2" Grid.Column="4">
<StackPanel HorizontalAlignment="Left"
VerticalAlignment="Center"
Orientation="Horizontal">
<Label Content="Height" />
<control:DoubleTextBox VerticalContentAlignment="Center" DoubleText="{Binding DataContext.Height.Value, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />
</StackPanel>
</DockPanel>
<DockPanel Grid.Row="2"
Grid.Column="2"
Margin="0,50,0,0"
Visibility="{Binding CenterVisibility, ElementName=userControl}">
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Label Background="White"
Content="Center"
FontWeight="Bold"
Foreground="Red" />
<control:DoubleTextBox Margin="5,0,5,0"
VerticalContentAlignment="Center"
DoubleText="{Binding DataContext.CenterX.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
Foreground="Red" />
<control:DoubleTextBox Margin="5,0,5,0"
VerticalContentAlignment="Center"
DoubleText="{Binding DataContext.CenterY.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
Foreground="Red" />
</StackPanel>
</DockPanel>
</Grid>
<ContentPresenter x:Name="Content" Content="{Binding ElementName=userControl, Path=Content}"/>
</Grid>
</DataTemplate>
</UserControl.ContentTemplate>
</UserControl>
public static IEnumerable<T> FindVisualChildren<T>(this DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
この問題の背景
現在、当プロジェクトboiler's Graphicsは扇形を描画するツール、その名も「扇形ツール」を開発しています。扇形ツールの描画する機能は開発できたのですが、描画した後、その図形の詳細情報を閲覧する機能を開発しようとしました。まずはその画面をご覧ください。
赤い点が扇形の中心点になります。もう一枚ご覧ください。
向きが逆になっています。扇形ツールは任意の角度で描画できるため、中心点が上にきたり下にきたりします。するとWidthが赤点やCenter Pointに重なってしまうのがわかるでしょうか?私は中心点が上にきたらWidthを図形の下へ表示させたい、あるいは中心点が下にきたらWidthを図形の上へ表示させたいのです。(今はWidthを上辺に固定しています。)
そこで機能するはずなのが、DetailPieクラスのWidthPlacementプロパティです。このプロパティの中でWidthと中心点のHitTestを実施し、重なっていれば対岸へ追いやりたいと思っています。
該当するソースコード
ブランチ:feature/#25