I'm trying to load a Grid
with UIElement
s and/or FrameworkElement
s that have Style
and DataTrigger
s.
The following is a simple native XAML which works as expected; toggling the check box through the 3 states give three different styles to the Rectangle (see image at bottom).
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:XamlVsLoad"
mc:Ignorable="d"
Height="450" Width="800">
<CheckBox Name="MyCheck" IsChecked="True" Content="Checkbox" IsThreeState="True" Margin="10,10,0,0" Width="200"/>
<Rectangle Height="100" Width="100" StrokeThickness="5" Margin="10,50,100,100">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="White"/>
<Setter Property="Stroke" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyCheck, Path=IsChecked}" Value="True" >
<Setter Property="Fill" Value="Orange"/>
<Setter Property="Stroke" Value="Blue"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=MyCheck, Path=IsChecked}" Value="False" >
<Setter Property="Fill" Value="Blue"/>
<Setter Property="Stroke" Value="Orange"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
It also works when I save the same text to a file and load it dynamically into Window
's Viewbox
s with XAML like this:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0">
<DockPanel>
<Label Content="Native" DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10,10,10,10" />
<Viewbox Name="NativeViewbox" Height="auto" Width="200" DockPanel.Dock="Top"/>
<!-- Native Grid Omitted for brevity 3 uniquely named checkboxes -->
</Viewbox>
</DockPanel>
<DockPanel Grid.Column="1">
<DockPanel>
<Label Content="Dynamic" DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10,10,10,10" />
<Viewbox Name="DynamicViewbox" Height="auto" Width="200" DockPanel.Dock="Top"/>
</DockPanel>
</DockPanel>
<DockPanel Grid.Column="2">
<DockPanel>
<Label Content="Clone" DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10,10,10,10" />
<Viewbox Name="CloneViewbox" Height="auto" Width="200" DockPanel.Dock="Top" />
</DockPanel>
</DockPanel>
<DockPanel Grid.Column="3">
<DockPanel>
<Label Content="Saved" DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10,10,10,10" />
<Viewbox Name="SavedViewbox" Height="auto" Width="200" DockPanel.Dock="Top"/>
</DockPanel>
</DockPanel>
However, if I try to copy/clone/deepcopy (I've tried several different methods) the FrameworkElements
/UIElements
from one Grid
/container to a new one, the style no longer works. Here are the various ways I'm currently loading and cloning them:
public partial class ReadCopy : Window {
public ReadCopy() {
InitializeComponent();
// test the dynamic load
Grid NativeXaml = ReadGrid("C:\\XamlFiles\\LoadXaml.xaml");
DynamicViewbox.Child = NativeXaml; // honors style
// test the Clone load
Grid CloneXaml = new Grid();
foreach (FrameworkElement fe in NativeXaml.Children) CloneXaml.Children.Add(Clone(fe));
CloneViewbox.Child = CloneXaml; // doesn't honor style
// test the save Clone and then load
StringBuilder outstr = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true, IndentChars = " ", NewLineChars = "\r\n", NewLineHandling = NewLineHandling.Replace };
XamlDesignerSerializationManager dsm = new XamlDesignerSerializationManager(XmlWriter.Create(outstr, settings)) { XamlWriterMode = XamlWriterMode.Expression };
XamlWriter.Save(CloneXaml, dsm);
File.WriteAllText("C:\\XamlFiles\\SavedXaml.xaml", outstr.ToString());
Grid SavedXaml = ReadGrid("C:\\XamlFiles\\SavedXaml.xaml");
SavedViewbox.Child = SavedXaml; // honors style and triggers again...
}
public Grid ReadGrid(string fn) {
FileStream fs = new FileStream(fn, FileMode.Open);
return XamlReader.Load(fs) as Grid;
}
public FrameworkElement Clone(FrameworkElement it) {
FrameworkElement clone;
using (var stream = new MemoryStream()) {
XamlWriter.Save(it, stream);
stream.Seek(0, SeekOrigin.Begin);
clone = (FrameworkElement)XamlReader.Load(stream);
}
clone.Style = it.Style; // setting it or not has no effect
return clone;
}
}
The output of the beginning simple example grid's cloned XAML via XamlWriter.Save:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CheckBox IsChecked="True" IsThreeState="True" Style="{x:Null}" Name="MyCheck" Width="200" Margin="10,10,0,0">Checkbox</CheckBox>
<Rectangle StrokeThickness="5" Width="100" Height="100" Margin="10,50,100,100">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked, ElementName=MyCheck}" Value="True">
<Setter Property="Shape.Fill">
<Setter.Value>
<SolidColorBrush>#FFFFA500</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Shape.Stroke">
<Setter.Value>
<SolidColorBrush>#FF0000FF</SolidColorBrush>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsChecked, ElementName=MyCheck}" Value="False">
<Setter Property="Shape.Fill">
<Setter.Value>
<SolidColorBrush>#FF0000FF</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Shape.Stroke">
<Setter.Value>
<SolidColorBrush>#FFFFA500</SolidColorBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
<Style.Resources>
<ResourceDictionary />
</Style.Resources>
<Setter Property="Shape.Fill">
<Setter.Value>
<SolidColorBrush>#FFFFFFFF</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Shape.Stroke">
<Setter.Value>
<SolidColorBrush>#FF000000</SolidColorBrush>
</Setter.Value>
</Setter>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
While I can see it reformatted the Style
, I don't understand why it no longer works when I set the Viewbox.Child
with the clone. What's even more confusing is that the loading the saved file of the clone then works. Here's what all four (Native, Dynamic, Cloned, Saved Clone Reloaded) look like:
Can anyone explain how to properly preserve the style through a copy/clone?
The Style
on the Rectangle
is triggered by the CheckBox
. Because of this, Clone()
will not work loading the child elements separately. To load them at the same time, clone their parent, the Grid
itself. I reproduced your problem and this worked for me:
Grid NativeXaml = ReadGrid();
DynamicViewbox.Child = NativeXaml;
Grid CloneXaml = (Grid)Clone(NativeXaml);
CloneViewbox.Child = CloneXaml;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With