WPF - Border with a OpacityMask/VisualBrush: Memory Leaks - memory-leaks

A brief explanation about my app:
the application in which I'm working on is such a greeting cards designer. Imagine something in which there is a background image, and an indefinite number of "layers" (in particular, pictures) that stay over the background and can be moved, resized, moved front and back, etc...
It is also possibile to apply particular shapes to these layers, like a star, an ellipse, .. and after the card is made, it's possibile to save is to jpeg file.
The problem
Everything works correctly, but I detected that when a shape is applied to a layer, a memory leak is generated.
Here is the code of the UserControl of each layer:
<UserControl>
.....
<Grid x:Name="_myGrid" >
<Border x:Name="im_the_problem" BorderThickness="0" OpacityMask="{Binding Path=MyMask.Data, Converter={StaticResource MaskConverter}}">
<!-- My Image... -->
</Border>
</Grid>
</UserControl>
where MaskConverter code is the following:
public class MaskConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
String maskData = value as String;
if (maskData == null)
return null;
if (maskData == "")
return null;
VisualBrush vb = new VisualBrush();
vb.Visual = XamlReader.Parse(maskData) as Visual;
return vb;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
The Parameter "MyMask.Data" is a XAML Path (that is the shape that I'm applying) that I dinamically load from a textfile that contains different shapes.
So, the principle is that if I have the border named *im_the_problem*, the memory is NOT released. If I comment *im_the_problem* (so I'll just have rectangular layers/pictures without shapes) everything work like a charm, without memory leaks.
The problem should be in the OpacityMask + VisualBrush.
Am I doing something wrong?
Or is there a known problem? Is there a way to do the same (apply a shape to a picture..) in a different manner?
Thanks.

You might be able to try binding the MyMask.Data to an actual Path.Data, and setting the Path.Fill to an ImageBrush created from the image?

You need to freeze your VisualBrush ;)

I had this problem in a DataGrid's column template where I was using a <Canvas><Path /></Canvas> (as a static-resource) into a VisualBrush (also a static-resource) and using that as the OpacityMask for a Rectangle. Whenever the DataGrid was reloaded the Rectangle wouldn't release VisualBrush references to the OpacityMask, I used a memory-profiler tool to reveal that all the VisualBrush objects were using the bulk of memory.
I don't understand why or how this happened - but I'm glad I'm not alone (even if I had the same problem some 6.5 years later...).
My XAML was something like this:
<DataGrid.Resources>
<Canvas x:Key="icon" ...>
<Path ... />
</Canvas>
<VisualBrush x:Key="iconBrush" Stretch="Uniform" Visual="{StaticResource icon}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Rectangle
Fill="{Binding Foreground, ElementName=myDataGrid}"
Width="14"
Height="14"
Margin="4"
Visibility="{Binding IconVisibility}"
OpacityMask="{StaticResource iconBrush}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...
</DataGrid.Columns>
I read that setting IsFrozen = true (done using this technique: https://www.codeproject.com/Tips/72221/Freeze-brushes-directly-in-the-XAML-to-improve-you ) would help memory issues with Brushes, however this seemingly had no effect at all. Weird.
I thought I'd experiment and I reasoned that if the issue was leaking the VisualBrush then I wondered if having it as a StaticResource was messing with object-references, so I changed it to an "owned" object, like so:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Rectangle
Fill="{Binding Foreground, ElementName=myDataGrid}"
Width="14"
Height="14"
Margin="4"
Visibility="{Binding IconVisibility}"
>
<VisualBrush Stretch="Uniform" Visual="{StaticResource iconBrush}" />
</Rectangle>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
This fixed the issue! And I still don't know why - I wonder if it's a bug in WPF?
On a related note, I came to realise that using a VisualBrush was overkill as I'm rendering just a simple Path - VisualBrush is expensive because it renders an entire WPF view - I also learned from other documentation that Path itself isn't necessary for rendering simple shapes because itself is a complete UIElement and FrameworkElement - which are "heavier" types.
I changed my code to store the path in a PathGeometry value inside a GeometryDrawing static-resource which is loaded into a DrawingBrush:
<GeometryDrawing x:Key="iconDrawing" Brush="Black" Geometry="..." />
<Rectangle
Fill="{Binding Foreground, ElementName=myDataGrid}"
Width="14"
Height="14"
Margin="4"
Visibility="{Binding IconVisibility}"
OpacityMask="{StaticResource iconBrush}"
>
<DrawingBrush Stretch="Uniform" Drawing="{StaticResource iconDrawing}" />
</Rectangle>
Doing this also made a dent in memory usage, and hopefully, performance.
In your project I see you're not using the path information as a resource, but the same technique applies: load your path into a PathGeometry (or rather, StreamGeometry object, which is even faster and is meant for immutable geometry) and set that as the Drawing for a DrawingBrush.

Related

Add a disabled/ non-selectable separator in a bound ComboBox

I have a ComboBox and I want to add a separator which is disabled, non-selectable.
Standard functions such as add, edit, delete should be selectable under the separator.
The items containing an image and text should be displayed above the separator.
<ComboBox Name="cmbTest">
<!--These items should be loaded by code or ViewModel.-->
<ComboBoxItem >Item1</ComboBoxItem>
<ComboBoxItem >Item2</ComboBoxItem>
<ComboBoxItem >Item3</ComboBoxItem>
<!--Separator which is disabled.-->
<ComboBoxItem IsEnabled="False" >
<NavigationViewItemSeparator BorderThickness="1"></NavigationViewItemSeparator>
</ComboBoxItem>
<!--Menu below the separator.-->
<ComboBoxItem x:Name="cmbiNewApplication">Neu</ComboBoxItem>
<ComboBoxItem x:Name="cmbiEdit">Bearbeiten</ComboBoxItem>
<ComboBoxItem x:Name="cmbiDelete">Löschen</ComboBoxItem>
<ComboBoxItem x:Name="cmbiImportApplication">Import</ComboBoxItem>
<ComboBoxItem x:Name="cmbiExportApplication" >Export</ComboBoxItem>
</ComboBox>
Currently, my ComboBox is tied to a ViewModel and I've created a TemplateSelector on advice. Unfortunately, the ComboBoxItem in the template doesn't seem to be the generated ComboBoxItem itself, so the IsEnabled property has no effect.
<ComboBox x:Name="cmbApplication"
CornerRadius="3"
Width="300"
ItemsSource="{x:Bind AppProcessMainViewModel.AppProcesses}"
Tapped="cmbApplication_Tapped" >
<ComboBox.ItemTemplateSelector>
<local:MenuDataTemplateSelector>
<local:MenuDataTemplateSelector.DefaultTemplate>
<DataTemplate x:DataType="local:AppProcess" >
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal" Opacity="{x:Bind BoolToOpacity(Activated), Mode=OneWay}">
<TextBlock Text="{x:Bind Description, Mode=OneWay}"></TextBlock>
</StackPanel>
</StackPanel>
</DataTemplate>
</local:MenuDataTemplateSelector.DefaultTemplate>
<local:MenuDataTemplateSelector.MenuTemplate>
<DataTemplate x:DataType="local:AppProcess">
<ComboBoxItem IsEnabled="False" Height="22" >
<NavigationViewItemSeparator BorderThickness="1"></NavigationViewItemSeparator>
</ComboBoxItem>
</DataTemplate>
</local:MenuDataTemplateSelector.MenuTemplate>
</local:MenuDataTemplateSelector>
</ComboBox.ItemTemplateSelector>
</ComboBox>
My consideration was that I set the IsEnabled property of the item in the Tapped event of the combo box.
Unfortunately, I really can't figure out how to get the property of the ComboBoxItem by index.
How can I set the property?
Is this really the best approach (TemplateSelector and setting the property in the Tapped event)?
I've already read a lot about this and unfortunately there seems to be a lot missing in WinUI 3 in contrast to UWP/WPF that make things much easier.
I'm trying to get the item via the index and cast it, but there seems to be another problem, maybe because of the ViewModel?
Something like...
//Error
auto cb = cmbApplication().Items().GetAt(1).as<winrt::Microsoft::UI::Xaml::Controls::ComboBoxItem>();
I came across this site of the MS documentation (Find DataTemplate Generated Elements) and just tried it out, which works wonderfully... happy new year!
auto item = cmbApplication().ItemContainerGenerator().ContainerFromItem(cmbApplication().Items().GetAt(3));
item.as< winrt::Microsoft::UI::Xaml::Controls::ComboBoxItem>().IsEnabled(false);

win10 app - Rounded corner images keeping aspect ratio

Simple app that just displays a list of images. The list of images must keep there aspect ratio, but be full window width + a bit of margin. The hard part is making the images additionally have rounded corners.
Ideas?
Only success I have had is with ImageBrush, but any control using that doesn't keep the aspect ratio. For example, here you must set the height and width.
<Rectangle RadiusX="10" RadiusY="10" Stretch="Fill" Width="100" Height="100" >
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding}"></ImageBrush>
</Rectangle.Fill>
</Rectangle>
full source here: http://1drv.ms/1HlZHVe
I got nicely rounded corners on my images (in my UWP app) by using an ImageBrush, however be careful if you do this programmatically - the first time I did I managed to use my memory rather poorly and consumed too much (poor coding).
I was using the ImageBrush much as you seem to be, but I wasn't getting any distortion of the aspect ratio; make sure you're setting properties like Stretch appropriately - e.g. Stretch.UniformToFill.
<Rectangle x:Name="rctCardFace" Margin="0" RadiusX="20" RadiusY="20" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<ImageBrush Stretch="UniformToFill"/>
</Rectangle.Fill>
</Rectangle>
Or in C#
Uri path = new Uri(_ActiveStyle.BackgroundImagePath, UriKind.Absolute);
BitmapImage bitmapImage = new BitmapImage(path);
if (_AppReference.CardManager.TempImgBrush == null)
{
_AppReference.CardManager.TempImgBrush = new ImageBrush();
}
_AppReference.CardManager.TempImgBrush.ImageSource = bitmapImage;
_AppReference.CardManager.TempImgBrush.Stretch = _ActiveStyle.BackgroundImageStretch;
_AppReference.CardManager.TempImgBrush.AlignmentX = _ActiveStyle.BackgroundImageAlignX;
_AppReference.CardManager.TempImgBrush.AlignmentY = _ActiveStyle.BackgroundImageAlignY;
cfPreview.ImgB = _AppReference.CardManager.TempImgBrush;
Hi you can use the following code to create a rounded corner image in UWP:
<Ellipse Width="250" Height="250">
<Ellipse.Fill>
<ImageBrush ImageSource="ms-appx:///highfive.jpg" />
</Ellipse.Fill>
</Ellipse>

Windows Phone Runtime pathicon runtime vs designtime

I’m working on a WP8.1 runtime app (using the universal app template).
I’ve created an appbar with a togglebutton. The button icon is a PathIcon.
in designtime (both Blend and VS2013) the button looks exactly what I expect it to look like but at runtime the path stretches out so it fills the circle of the button.
is this difference between designtime and runtime a known issue and is there a workaround so that my button looks how it’s supposed to look at runtime?
It looks good on every resolution in Blend so it's not a scaling issue
designtime:
runtime:
the XAML for the button:
<Page.BottomAppBar>
<CommandBar>
<AppBarToggleButton Label="HQ">
<AppBarToggleButton.Icon>
<PathIcon HorizontalAlignment="Left"
VerticalAlignment="Top"
Data="M21.0007,6.25 C6.3752,1.125 5.25043,16.6091 5.62518,19.5 C5.9215,21.7858 6.25018,10.75 16.0005,12.875 C19.8337,13.7104 24.1247,19.3755 20.3747,23.0006 C20.1689,23.1995 18.7133,17.0212 13.6254,19.5002 C8.75026,21.8755 13.0958,28.4331 12.8234,28.4006 C5.88184,27.5722 0.5,21.6646 0.5,14.5 C0.5,6.76801 6.76801,0.5 14.5,0.5 C19.153,0.5 23.0847,2.9988 25.2497,6.50016 C25.7361,7.28663 25.9271,7.98672 26.2501,8.87522 C26.75,10.2503 22.9202,6.92261 21.0007,6.25 z" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
</CommandBar>
I did not test with a PathIcon, but you can just a Path just fine:
<CommandBar>
<AppBarToggleButton Label="HQ">
<Path VerticalAlignment="Center" HorizontalAlignment="Center"
Fill="White" Stroke="White"
Data="M21.0007,6.25 C6.3752,1.125 5.25043,16.6091 5.62518,19.5 C5.9215,21.7858 6.25018,10.75 16.0005,12.875 C19.8337,13.7104 24.1247,19.3755 20.3747,23.0006 C20.1689,23.1995 18.7133,17.0212 13.6254,19.5002 C8.75026,21.8755 13.0958,28.4331 12.8234,28.4006 C5.88184,27.5722 0.5,21.6646 0.5,14.5 C0.5,6.76801 6.76801,0.5 14.5,0.5 C19.153,0.5 23.0847,2.9988 25.2497,6.50016 C25.7361,7.28663 25.9271,7.98672 26.2501,8.87522 C26.75,10.2503 22.9202,6.92261 21.0007,6.25 z" />
</AppBarToggleButton>
</CommandBar>

How do specify a width percentage in JavaFX 2 using FXML?

I am looking for a way to say set a maxWidth size to 80% in FXML.
Much like in web development.
<VBox fx:id="testVB" prefWidth="600">
But this does not:
<VBox fx:id="testVB" prefWidth="80%">
I know that in Straight JavaFX2 non-fxml you can create insets? What is the best way to do this outside of code in FMXL?
Thanks!
Riley
I'm not sure you can. You need to use the GridPane layout component. In this component, you can specify rows and columns constraints, and in these constraints you can specify a width as a percentage. For example:
<GridPane>
<children>
<TitledPane text="testGridPane" GridPane.columnIndex="0" GridPane.rowIndex="0" />
</children>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="80.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="20.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
This code defines a GridPane with a first column with a width of 80%. The TitledPane is set in the first cell of the first column of this GridPane, and can (because you need to be sure that the width constraints of the TitledPane match your needs) occupy 80% of the width of the GridPane.
Please note that I removed all information not relevant to your question. By the way, Oracle's Scene Builder tool is very useful to define complex FXML layout.
It seems like many answers have already been provided and they should work. However, there is a way to set percentages:
<fx:define>
<Screen fx:factory="getPrimary" fx:id="screen" />
</fx:define>
This would help you detect the dimensions of the current screen, the application is being displayed on. Now that we have the display dimensions, we can play with it in FXML as follows:
<HBox fx:id="hroot" prefHeight="${screen.visualBounds.height}" prefWidth="${screen.visualBounds.width}"> Your FXML elements inside the root... </HBox>
Note that I use visualBounds, since this would get me the available space on the screen, since I don't want an overlap with the taskbar in Windows for example. For fullscreen applications, you would just use 'bounds'.
Now, to come to your point of using percentages, you can actually play with the value of the prefheight and prefWidth. You can put calculations inside the ${}.
Optionally:
If you want to have all your elements use relative sizes, just refer to them, using their ID and width or height property, and make your calculation.
<VBox fx:id="VBSidebar" prefWidth="${hroot.width*0.15}" prefHeight="${hroot.height}"> more elements.. </VBox>
Hope this helps!
You can simulate it - basic example that simulates 50% for two cols in an HBox. You can add dummy panes to get thirds, etc.
HBox {
VBox {
static hgrow : "ALWAYS",
Label {
text : "Privacy",
alignment : "CENTER",
styleClass : ["h2", "heading"]
}
},
VBox {
static hgrow : "ALWAYS",
Label {
text : "Messages",
alignment : "CENTER",
styleClass : ["h2", "heading"]
},
Label {text:""}
}
}

Silverlight control InitializeComponent freezes application

I have a control that I create hundreds of times during the application.
I have noticed that my app freezes because in the Initializecomponent function,
there is
System.Windows.Application.LoadComponent(this, new System.Uri("/fa;component/Controls/Common/Popup/PopupItem.xaml", System.UriKind.Relative));
if I comment this out, the application runs smoothly (of course without the control rendered).
How can I avoid/increase performance so the XAML won't be loaded each time, but somehow to recycle the control??
for (int i = 5; i < colValues.Count; i++)
{
if (colValues[i].Count == "1")
continue;
PopupItem pi = new PopupItem(colValues[i], false, this, FilterCategorySearch.PopupContent);
FilterCategorySearch.PopupContent.spItemsContainer.Children.Add(pi);
}
and the XAML is
<UserControl x:Class="FacetedSearch.Controls.Common.Popup.PopupItem"
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:FacetedSearch.Controls.Common"
mc:Ignorable="d">
<UserControl.Resources>
<SolidColorBrush x:Key="TextNormalBrush" Color="#FF656565"/>
<SolidColorBrush x:Key="TextHoverBrush" Color="#FFA39F9F"/>
</UserControl.Resources>
<StackPanel MouseEnter="LayoutRoot_MouseEnter" MouseLeave="LayoutRoot_MouseLeave" Orientation="Horizontal" Margin="0,4,0,0">
<local:CTLCheckBox x:Name="cbFilter" MouseLeftButtonUp="cbFilter_MouseLeftButtonUp" Cursor="Hand" Height="14" Width="10" Margin="4,0" />
<TextBlock x:Name="tbFilterName" Foreground="{StaticResource TextNormalBrush}" MouseLeftButtonUp="tbFilterName_MouseLeftButtonUp" TextWrapping="Wrap" FontFamily="Arial" Margin="0,0,4,0" Cursor="Hand"/>
<TextBlock x:Name="tbFilterCount" TextWrapping="Wrap" Foreground="{StaticResource TextNormalBrush}" FontFamily="Arial"/>
</StackPanel>
thanks
The xaml for UserControls is parsed by Silverlight for every new instance of the UserControl. This means that if you add 100 instances of the same UserControl, the xaml will be read, parsed, instantiated as objects then visual objects 100 times.
You have 2 possibilities:
Access your UserControl from another location by referencing it from within a DataTemplate (used by means of, say, a ContentControl)
Rewrite your UserControl to be a "real" control (i.e. a sublass of Control or ContentControl)

Resources