Converting ControlTemplate XAML to C# - c#

I have been stumped with trying to convert the following code into pure c#. This XAML code is from Cavanaghs blog on how to make rounded corners on anything. The code works but I need to convert it to c# as i need it to be dynamic in some cases. If you could help that would be great.
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType='{x:Type ListViewItem}'>
<Grid>
<Border CornerRadius="15" Name="mask" Background="White"/>
<StackPanel Background="Beige">
<StackPanel.OpacityMask>
<VisualBrush Visual="{Binding ElementName=mask}"/>
</StackPanel.OpacityMask>
<GridViewRowPresenter Content="{TemplateBinding Content}" Columns="{TemplateBinding GridView.ColumnCollection}"/>
<TextBlock Background="LightBlue" Text="{Binding News}" />
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
So far I have the following but I am getting errors.
FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border));
border.SetValue(Border.BackgroundProperty, Brushes.White);
border.SetValue(Border.CornerRadiusProperty, new CornerRadius(8, 8, 8, 8));
border.SetValue(Border.NameProperty, "roundedMask");
As far as I can tell I cant make the VisualBrush as a FrameworkElementFactory (crashes), but if i declare it as a regular element VisualBrush i cant pass border in as a Visual since its a FrameworkElementFactory.
Simply i am getting lost, any help would be appreciated.
Thanks for any help

You don't actually have to convert this into C# to apply it dynamically. If you add it to your application resources, within your App.xaml file as follows:
<Application.Resources>
<ControlTemplate TargetType='{x:Type ListViewItem}' x:Key="MyListViewItemTemplate">
<Grid>
<Border CornerRadius="15" Name="mask" Background="White"/>
<StackPanel Background="Beige">
<StackPanel.OpacityMask>
<VisualBrush Visual="{Binding ElementName=mask}"/>
</StackPanel.OpacityMask>
<GridViewRowPresenter Content="{TemplateBinding Content}" Columns="{TemplateBinding GridView.ColumnCollection}"/>
<TextBlock Background="LightBlue" Text="{Binding News}" />
</StackPanel>
</Grid>
</ControlTemplate>
</Application.Resources>
Note the x:Key attribute which keys this item.
You can then look it up anywhere in your code ...
ControlTemplate template = this.Findresource("MyListViewItemTemplate") as ControlTemplate
You can then apply it as and when you need it!

You do not want to know this. Seriously, you don't, it's a nightmare.
Edit: If i did not make any mistake this is the translation of your code...
Setter setter = new Setter();
setter.Property = ListViewItem.TemplateProperty;
ControlTemplate template = new ControlTemplate(typeof(ListViewItem));
var grid = new FrameworkElementFactory(typeof(Grid));
var border = new FrameworkElementFactory(typeof(Border));
border.SetValue(Border.BackgroundProperty, Brushes.White);
border.SetValue(Border.NameProperty, "mask");
border.SetValue(Border.CornerRadiusProperty, new CornerRadius(15));
grid.AppendChild(border);
var stackPanel = new FrameworkElementFactory(typeof(StackPanel));
stackPanel.SetValue(StackPanel.BackgroundProperty, Brushes.Beige);
var visualBrush = new FrameworkElementFactory(typeof(VisualBrush));
visualBrush.SetBinding(VisualBrush.VisualProperty, new Binding() { ElementName = "mask" });
stackPanel.SetValue(StackPanel.OpacityMaskProperty, visualBrush);
var gridViewRowPresenter = new FrameworkElementFactory(typeof(GridViewRowPresenter));
gridViewRowPresenter.SetValue(GridViewRowPresenter.ContentProperty, new TemplateBindingExtension(GridViewRowPresenter.ContentProperty));
gridViewRowPresenter.SetValue(GridViewRowPresenter.ColumnsProperty, new TemplateBindingExtension(GridView.ColumnCollectionProperty));
stackPanel.AppendChild(gridViewRowPresenter);
var textBlock = new FrameworkElementFactory(typeof(TextBlock));
textBlock.SetValue(TextBlock.BackgroundProperty, Brushes.LightBlue);
textBlock.SetBinding(TextBlock.TextProperty, new Binding("News"));
stackPanel.AppendChild(textBlock);
grid.AppendChild(stackPanel);
template.VisualTree = grid;
setter.Value = template;
Edit: There is still a bug left, the VisualBrush cannot be created like that, the rest seems to work.

Related

TreeViewItem styles not being applied when selecting an item dynamically over via input

I have a treeview dynamically generated within the program. It uses properties on the class to select items by default if the user sets the preference for it:
However, when I do this, it applies the default style, rather than the current style, which is currently set and applies a AdonisUI dark mode style if requested, or light if not.
The Tree View (and Style) code:
<Window.Resources>
<Color x:Key="TitleBarColor">#FF191970</Color>
<Color x:Key="TitleBarForeColor">#FFFFFAF0</Color>
<Style x:Key="SystemTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</Window.Resources>
....
<TreeView Name="tvwSystemTree" Grid.Column="0" Grid.Row="0"
SelectedItemChanged="tvwSystemTree_SelectedItemChanged" ItemContainerStyle="{StaticResource SystemTreeViewItemStyle}"
Visibility="Hidden">
<TreeView.Style>
<Style TargetType="TreeView" BasedOn="{StaticResource {x:Type TreeView}}"/>
</TreeView.Style>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
DataType="{x:Type lAWObjects:SystemObject}" ItemsSource="{Binding Items}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding IconUri, Mode=OneWay}" Height="16" Width="16" />
<TextBlock Text="{Binding Title}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And in code-behind:
ObservableCollection<SystemObject> AstralObjects = new();
SystemObject root = new SystemObject() { Title = ourSystem.SystemName, IconUri = new Uri(SystemObject.SystemLogo) };
foreach(Star v in ourSystem.SystemStars)
{
SystemObject child = new SystemObject() { Title = v.Name, IconUri = new Uri(SystemObject.SunLogo) };
foreach (IOrbitalBody p in ourSystem.PlanetaryBodies)
{
if (p.Parent == v)
{
SystemObject child2 = new SystemObject() { Title = p.Name, IconUri = new Uri(SystemObject.PlanetLogo) };
child.Items.Add(child2);
}
}
root.Items.Add(child);
}
tvwSystemTree.ItemsSource = AstralObjects;
tvwSystemTree.Visibility= Visibility.Visible;
grdDetailView.Visibility = Visibility.Visible;
if (preferences.AutoDisplaySystem)
{
foreach (var v in AstralObjects)
{
if (v.Title == ourSystem.SystemName)
{
v.IsSelected = v.IsExpanded = true;
tvwSystemTree_SelectedItemChanged(this, new RoutedPropertyChangedEventArgs<object>(null, v));
}
}
}
For completion's sake, the SystemObject code that is probably most relevant is that it implements INotifyPropertyChanged. But I can provide it as well if requested.
When this code fires, it applies the normal blue-background and white-text. But if you click any option in the tree, it then applies the style specified colors.
I've tried specifiying that <Style x:Key="SystemTreeViewItemStyle" TargetType="{StaticResource {x:Type TreeViewItem}}"> but it appears AdonisUI doesn't support those properties on it. (And a code-search on github also appears to verify this.)
My only guess is that somehow the selection style is only applied on user interaction. Is there a way around this that I haven't figured out? I'm rather reluctant to apply explicit style colors so I don't have to create variations for any style I may apply in the future.
Update After some investigation I've found out it's because it's overriding Adonis's code (which makes sense) even if I attempt to apply it via {StaticResource {x:Key TreeViewItem}}, but is not respecting any changes I attempt to make via specified dynamic resource.

Adding controls to XAML Page by code

I have a XAML page with this body:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
</Grid>
Now I want to add following controls in the Code Behind into the ContentPanel
<ViewportControl x:Name="viewport" ManipulationStarted="OnManipulationStarted" ManipulationDelta="OnManipulationDelta" ManipulationCompleted="OnManipulationCompleted" ViewportChanged="viewport_ViewportChanged">
<Canvas x:Name="canvas">
<Image x:Name="TestImage" RenderTransformOrigin="0,0" CacheMode="BitmapCache" ImageOpened="OnImageOpened">
<Image.RenderTransform>
<ScaleTransform x:Name="xform"/>
</Image.RenderTransform>
</Image>
</Canvas>
</ViewportControl>
Is there a way to do this by code?
Since
Creating the ViewportControl is OK. Creating the Canvas is OK. But creating the Image and it's "transform-stuff" - here I'm unable to code this
and you don't need to register names you can try this to create Image:
var img = new Image
{
RenderTransformOrigin = new Point(0,0),
CacheMode = new BitmapCache(),
RenderTransform = new ScaleTransform()
};
img.ImageOpened += OnImageOpened;
//and you add it to Canvas
Canvas canvas = new Canvas();
canvas.Children.Add(img);

How to create green line border around an element in .cs file and XAML file

I would like to create a border over the WindowsFormsHost. How to do so?
In .cs file:
WindowsFormsHost Host = new WindowsFormsHost();
and in the xaml:
<WindowsFormsHost x:Name="Host"></WindowsFormsHost>
The System.Windows.Border class is a type of Decorator, which means it can have a single Child element. In this case, your child would be the WindowsFormsHost.
XAML:
<Border BorderBrush="Green" BorderThickness="1">
<WindowsFormsHost x:Name="Host"></WindowsFormsHost>
</Border>
Use the border class:
xaml:
<Border BorderThickness="1" BorderBrush="Green">
<WindowsFormsHost x:Name="Host"></WindowsFormsHost>
</Border>
cs:
var myBorder = new Border();
myBorder.BorderBrush = Brushes.Green;
myBorder.BorderThickness = new Thickness(1);
myBorder.Child = new WindowsFormsHost();

Assigning zIndex order to image and text elements on a canvas in Silverlight

I have an image editor in silverlight that allows the user to add, manipulate and delete image and text elements on a canvas. I notice it seems to act strangely when adding new elements sometimes and they will be placed behind an existing element for example. Below is the code for adding image elements, and the order elements method which it calls. I inherited this code from someone else so at times I can't follow what his intention was. The code doesn't seem to do what it is supposed to though.
Can someone suggest a better way to assign a Z-Index value to elements that I am adding?
XAML of my workspace canvas -
<Canvas x:Name="pnlCanvas" HorizontalAlignment="Center" VerticalAlignment="Center" Height="{Binding Path=CanvasHeight, Mode=OneWay,UpdateSourceTrigger=Default}"
Width="{Binding Path=CanvasWidth, Mode=OneWay, UpdateSourceTrigger=Default}" >
<ItemsControl ItemsSource="{Binding Path=Elements, Mode=OneWay}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="{Binding Path=CanvasBackground, Mode=OneWay}"
Height="{Binding Path=CanvasHeight, Mode=OneWay,UpdateSourceTrigger=Default}"
Width="{Binding Path=CanvasWidth, Mode=OneWay, UpdateSourceTrigger=Default}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Canvas>
Adding image element -
private void AddImageElement(object param)
{
bool? gotImage;
string fileName;
BitmapImage imageSource = GetImageFromLocalMachine(out gotImage, out fileName);
OrderElements();
if (gotImage == true)
{
Image image = new Image();
image.Name = fileName;
image.Source = imageSource;
image.Height = imageSource.PixelHeight;
image.Width = imageSource.PixelWidth;
image.MaxHeight = imageSource.PixelHeight;
image.MaxWidth = imageSource.PixelWidth;
image.Cursor = Cursors.Hand;
image.Tag = null;
AddDraggingBehavior(image);
image.MouseLeftButtonUp += element_MouseLeftButtonUp;
this.Elements.Add(image);
numberOfElements++;
this.SelectedElement = image;
this.SelectedImageElement = image;
}
}
Order Elements -
private void OrderElements()
{
var elList = (from element in this.Elements
orderby element.GetValue(Canvas.ZIndexProperty)
select element).ToList<FrameworkElement>();
for (int i = 0; i < elList.Count; i++)
{
FrameworkElement fe = elList[i];
fe.SetValue(Canvas.ZIndexProperty, i);
}
this.Elements = new ObservableCollection<FrameworkElement>(elList);
}
My end intention once I have this sorted out is to include a layers container like in Photoshop etc. where I will be able to reorder the elements. Hopefully someone can help get me moving in that direction. Basically how do I set the Z-Index correctly because I don't think this is doing it.
An ItemsControl wraps each element in a <ContentPresenter> tag, so although the ZIndex is set on your element, it doesn't get applied because the ZIndex on the ContentPresenter is what matters
What actually gets rendered looks like this:
<Canvas>
<ContentPresenter>
<Image Canvas.ZIndex="0" />
</ContentPresenter>
<ContentPresenter>
<Image Canvas.ZIndex="1" />
</ContentPresenter>
...
</Canvas>
To fix the issue, set the ZIndex in the ItemContainerStyle so it gets applied to the ContentPresenter instead of the UI Element
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.ZIndex" Value="{Binding Canvas.ZIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
For more information, see the bottom section of my blog post about WPF's ItemsControl
Edit:
Apparently Silverlight doesn't have an ItemsContainerStyle for the ItemsControl.
In that case, simply set an implicit style for the ContentPresenter that sets the Canvas.ZIndex value in your ItemsControl.Resources
<ItemsControl.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.ZIndex" Value="{Binding Canvas.ZIndex}" />
</Style>
</ItemsControl.Resources>

findResource() not working with DataTemplate

I defined a DataTemplate the following way:
<s:SurfaceWindow.Resources>
<ImageBrush x:Key="WindowBackground" Stretch="None" Opacity="0.6" ImageSource="pack://application:,,,/Resources/WindowBackground.jpg"/>
<DataTemplate x:Key="ContainerItemTemplate">
<Grid>
<Border BorderThickness="1" BorderBrush="White" Margin="3">
<s:SurfaceTextBox IsReadOnly="True" Width="120" Text="{Binding Path=name}" Padding="3"/>
</Border>
<s:SurfaceButton Content="Expand" Click="SourceFilePressed"></s:SurfaceButton>
</Grid>
</DataTemplate>
</s:SurfaceWindow.Resources>
Then I used it to add a ItemTemplate to a LibraryContainer:
<Grid Name="RootGrid" Background="{StaticResource WindowBackground}" >
<s:ScatterView Name="RootScatter">
<Viewbox>
<s:LibraryContainer Name="RootContainer" Grid.Row="0" ViewingMode="Bar">
<s:LibraryContainer.BarView>
<s:BarView Rows="2" NormalizedTransitionSize="2.5,0.8" ItemTemplate="{StaticResource ContainerItemTemplate}">
</s:BarView>
</s:LibraryContainer.BarView>
<s:LibraryContainer.StackView>
<s:StackView NormalizedTransitionSize="1,1" ItemTemplate="{StaticResource ContainerItemTemplate}">
</s:StackView>
</s:LibraryContainer.StackView>
</s:LibraryContainer>
</Viewbox>
</s:ScatterView>
</Grid>
Later, I add a new ScatterViewItem to the ScatterView:
ScatterViewItem item = new ScatterViewItem();
FrameworkElement element = surfaceWindow as FrameworkElement;
DataTemplate tmpl = element.FindResource("ContainerItemTemplate") as DataTemplate;
LibraryContainer container = new LibraryContainer();
container.ViewingMode = LibraryContainerViewingMode.Bar;
container.ItemsSource = itms;
container.BarView.ItemTemplate = tmpl;
item.Content = container;
surfaceWindow.getRootScatter().Items.Add(item);
Unfortunately, I always get a NullReferenceException inthe line:
container.BarView.ItemTemplate = tmpl;
Additional Information:
The object surfaceWindow is passed in this method. It is a reference to the file where i have defined DataTemplate.
Unless your constructor for your LibraryContainer object builds the LibraryContainer.BarView object, it is going to be null at that point.
Edit
Ok, so ignore the previous attempts...I've done a little more reading on the surface controls now.
Going back to your original method of fetching the data template via the key:
DataTemplate tmpl = element.FindResource("ContainerItemTemplate")
If you set a breakpoint there, was the template being returned or was it null at this point?

Categories