XAML ResourceDictionary で定義されているパスを実行時に WPF フォームに複数回追加するにはどうすればよいですか?
-
21-09-2019 - |
質問
XAML でパスが定義されています。
<UserControl.Resources>
<ResourceDictionary>
<Path x:Key="N44" Width="20" Height="80" Stretch="Fill" Fill="#FF000000" Data="M 20,25.2941L 20,29.4118L 15.9091,29.4118L 15.9091,40L 12.2727,40L 12.2727,29.4118L 2.54313e-006,29.4118L 2.54313e-006,25.6985L 13.4872,7.62939e-006L 15.9091,7.62939e-006L 15.9091,25.2941L 20,25.2941 Z M 12.2727,25.2941L 12.2727,5.28493L 2.09517,25.2941L 12.2727,25.2941 Z M 20,65.2941L 20,69.4118L 15.9091,69.4118L 15.9091,80L 12.2727,80L 12.2727,69.4118L -5.08626e-006,69.4118L -5.08626e-006,65.6985L 13.4872,40L 15.9091,40L 15.9091,65.2941L 20,65.2941 Z M 12.2727,65.2941L 12.2727,45.2849L 2.09517,65.2941L 12.2727,65.2941 Z "/>
</ResourceDictionary>
</UserControl.Resources>
これを WPF Gird に追加したいのですが、次のように一度実行すると機能します:
System.Windows.Shapes.Path aPath = new System.Windows.Shapes.Path();
aPath = (System.Windows.Shapes.Path)this.Resources["N44"];
LayoutRoot.Children.Add(aPath);
ただし、このコードをボタンのクリック イベントに追加してボタンを 2 回クリックすると、次のようなエラーがスローされます。
「指定されたビジュアルは、すでに別の視覚の子または組成ターゲットのルートです。」
次に、リソースの 2 つのインスタンスを作成しようとしましたが、引き続き同じエラーが発生します。以下はこのテストに使用したコードです。
private void cmbTest_Click(object sender, System.Windows.RoutedEventArgs e)
{
System.Windows.Shapes.Path aPath = new System.Windows.Shapes.Path();
aPath = (System.Windows.Shapes.Path)this.Resources["N44"];
if (LayoutRoot.Children.Contains(aPath) == true){
System.Windows.Shapes.Path bPath = (System.Windows.Shapes.Path)this.Resources["N44"];
LayoutRoot.Children.Add(bPath);
}else{
aPath.Name = "a";
aPath.Tag = "a";
LayoutRoot.Children.Add(aPath);
}
}
そのため、ResourceDictionary で定義されている XAML パスを実行時に WPF フォームに複数回追加するにはどうすればよいでしょうか?
解決 2
その後、重要な部分を見逃していたことに気づきました MSDN のドキュメント:
共有可能なタイプと UIElement タイプ:
リソース辞書は、XAMLでこれらのタイプの共有可能なタイプと値を定義するための手法です。すべてのタイプまたは値がリソースの使用に適しているわけではありません。
In particular, all UIElement derived types are not shareable unless they come from templates and application of a template on a specific control instance. Excluding the template case, a UIElement is expected to only exist in one place in an object tree once instantiated, and having a UIElement be shareable would potentially violate this principle.
要約すると、それはそのようには機能しません。 新しいインスタンスは作成されません そのコードを実行するたびに、オブジェクトへの参照が作成されるだけです。そのため、コードは 1 回だけは機能しますが、複数回は機能しません。そこで、もう少し読んだ結果、問題を解決するための 3 つの方法が考えられました。
1) 新しいオブジェクトへのディープ コピーを作成する手法を使用します。他の StackOverflow 質問の例 - ディープ クローン オブジェクト
2) XAML をアプリケーション内の文字列に保存し、XAML リーダーを使用してパスのインスタンスを作成します。
System.Windows.Shapes.Path newPath = (System.Windows.Shapes.Path)System.Windows.Markup.XamlReader.Parse("<Path xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' Width='20' Height='80' Stretch='Fill' Fill='#FF000000' Data='M 20,25.2941L 20,29.4118L 15.9091,29.4118L 15.9091,40L 12.2727,40L 12.2727,29.4118L 2.54313e-006,29.4118L 2.54313e-006,25.6985L 13.4872,7.62939e-006L 15.9091,7.62939e-006L 15.9091,25.2941L 20,25.2941 Z M 12.2727,25.2941L 12.2727,5.28493L 2.09517,25.2941L 12.2727,25.2941 Z M 20,65.2941L 20,69.4118L 15.9091,69.4118L 15.9091,80L 12.2727,80L 12.2727,69.4118L -5.08626e-006,69.4118L -5.08626e-006,65.6985L 13.4872,40L 15.9091,40L 15.9091,65.2941L 20,65.2941 Z M 12.2727,65.2941L 12.2727,45.2849L 2.09517,65.2941L 12.2727,65.2941 Z ' HorizontalAlignment='Left' VerticalAlignment='Top' Margin='140,60,0,0'/>");
LayoutRoot.Children.Add(newPath);
3) パス データのみをリソース ディクショナリに保存します。コードでパスの新しいインスタンスを作成し、パス データを新しいパスに適用してから、必要な他のプロパティを手動で追加します。
XAML - パス データは StreamGeometry として保存されます。
<UserControl.Resources>
<ResourceDictionary>
<StreamGeometry x:Key="N44">M 20,25.2941L 20,29.4118L 15.9091,29.4118L 15.9091,40L 12.2727,40L 12.2727,29.4118L 2.54313e-006,29.4118L 2.54313e-006,25.6985L 13.4872,7.62939e-006L 15.9091,7.62939e-006L 15.9091,25.2941L 20,25.2941 Z M 12.2727,25.2941L 12.2727,5.28493L 2.09517,25.2941L 12.2727,25.2941 Z M 20,65.2941L 20,69.4118L 15.9091,69.4118L 15.9091,80L 12.2727,80L 12.2727,69.4118L -5.08626e-006,69.4118L -5.08626e-006,65.6985L 13.4872,40L 15.9091,40L 15.9091,65.2941L 20,65.2941 Z M 12.2727,65.2941L 12.2727,45.2849L 2.09517,65.2941L 12.2727,65.2941 Z</StreamGeometry>
</ResourceDictionary>
</UserControl.Resources>
次にインスタンスを作成し、他の値を適用するための C# コード:
System.Windows.Shapes.Path bPath = new System.Windows.Shapes.Path();
bPath.Data = (System.Windows.Media.Geometry)this.FindResource("N44");
bPath.Width = 20;
bPath.Height = 80;
bPath.VerticalAlignment = System.Windows.VerticalAlignment.Top;
bPath.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
left = left + 40;
System.Windows.Thickness thickness = new System.Windows.Thickness(left,100,0,0);
bPath.Margin = thickness;
bPath.Fill = System.Windows.Media.Brushes.Black;
LayoutRoot.Children.Add(bPath);
他のヒント
ビルトインこれを行う方法、簡単にはあります。 集合X:共有=リソースの「偽」。これは、再利用できるようになります。あなたが望むようその後何回もそれを使用します。
<UserControl.Resources>
<ResourceDictionary>
<Path x:Shared="False" x:Key="N44" Width="20" Height="80" Stretch="Fill" Fill="#FF000000" Data="..."/>
</ResourceDictionary>
</UserControl.Resources>
ただ、パスのスタイルを作成し、それを適用します。