Error trying to freeze a Rectangle filled with a BitmapCacheBrush - wpf

I'm trying to Freeze a BitmapCacheBrush, but when I call Freeze it errors stating it can't be frozen.
What I'm trying to do is fill a Rectangle with a BitmapCacheBrush and then at some point, Freeze it so I can then animate the Rectangle and reuse the source Grid for something else (whilst animating the Rectangle).
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*"></RowDefinition>
<RowDefinition Height="50*"></RowDefinition>
</Grid.RowDefinitions>
<Grid x:Name="LISTING">
<Grid.CacheMode>
<BitmapCache RenderAtScale="1" SnapsToDevicePixels="True"/>
</Grid.CacheMode>
<Rectangle x:Name="Rectangle1" Fill="Red" />
<Label Content="Test" FontSize="20" Foreground="Black" />
<Button x:Name="Button1" Click="Button1_Click"/>
</Grid>
<Rectangle Grid.Row="1" x:Name="Rectangle2" >
<Rectangle.Fill>
<BitmapCacheBrush x:Name="BMCB" Target="{Binding ElementName=LISTING}"/>
</Rectangle.Fill>
</Rectangle>
</Grid>
In my test I am trying to see if I can Freeze the BMCB BitmapCacheBrush when the button is clicked, then as a test I want to hide the button and still see the bottom rectangle intact.
I'm wanting to use BitmapCacheBrush for the performance aspect.
Is this even possible?
Thanks
Ben

I would say No: But my only source to prove that is the Freezable documentation in the msdn, which says:
A Freezable can't be frozen if any of the following are true:
It has animated or data bound properties.
It has properties set by a dynamic resource.
It contains Freezable sub-objects that can't be frozen.
So i guess you can't freeze it because of your Target="{Binding ElementName=LISTING}" binding.

Related

WPF Control, UserControl, Template confusion

Disclosure:
I am new to WPF, about a week into it.
Problem:
I am trying to modify the behavior of a GridSplitter, to make it snap to interesting positions, to show a label (that follows the splitter) with current position, to have a context menu driven from said label, etc. I have prototyped all of this successfully on one gridsplitter in one simple test application, with a combination of XAML and some code behind.
Of note is that because the GridSplitter can't host content, I placed the label in the same grid cell as the splitter so that they move together.
So far so good....
Now I wish to replicate my work so that I can use my new GridSplitter functionality in place of the native control in many locations, and furthermore, I wish to have two variants, a horizontal and a vertical. Sounds like inheritance...create a subclass derived from GridSplitter and add in the additional functionality. But all of the reading I have done leaves me wondering how to go about this, and if this is even possible without starting over again and building my own GridSplitter from scratch?
Ideas welcome. Until then I will resume the fetal position.
Thanks
This answer might help you resolve your issue: How to make GridSplitter to "snap" into another element?
By subscribing to the GridSplitterDragCompleted event, you can insert your logic to snap to "interesting" positions.
You should
create a new control derived from GridSplitter.
subscribe to DragCompleted event to implement snapping functionality like DLeh mentioned.
add a few new properties for Label , ContextMenu etc.
supply a style for your new control.
This answers how to place content in the splitter
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Row 0" Background="Orange"/>
<!--<GridSplitter Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" Height="20" Background="Purple"/>-->
<GridSplitter Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<GridSplitter.Template>
<ControlTemplate TargetType="{x:Type GridSplitter}">
<TextBlock Text="TextBlock splitter" Background="Yellow" FontWeight="Bold"/>
</ControlTemplate>
</GridSplitter.Template>
</GridSplitter>
<Button Grid.Row="2" Content="Row 0" Background="Salmon"/>
</Grid>

Listbox content not dynamically re-sizing when Listbox size changes

I've seen quite a few people asking this question and i feel like my question is slightly different. I have a listbox that holds a series of identical custom made user controls. These controls are added to the listbox at runtime. Now i currently have my listbox items resizing themselves properly upon first creation and insertion into the control.
Here is the strange part. If I resize the listbox the controls that have been visible previously are not resized to the new listbox width. In other words if i add 12 controls and the box only shows 4 (the rest are hidden by a scrollbar) then if i resize the box the first 4 controls will still be the original width and if i scroll then the other 8 will be the correct size. Also if i manipulate the list items in any way they resize themselves to the proper width automatically. SEE EDIT2
I've tried attaching to the sizeChanged event and issuing the following on both the listbox and the items but it has had no effect. I think i need to find some way of resetting the layout information for the listbox items but i can't find the command.
item.InvalidateArrange();
item.InvalidateMeasure();
Layers.UpdateLayout();
item.UpdateLayout();
I think this has something to do with the items i'm adding because even if i detach the items from the lisbox and then attach them they remain the wrong width.
Here is my listbox code:
<ListBox x:Name="Layers" VerticalContentAlignment="Top" Margin="0,17,0,0" BorderThickness="0,1,0,0" HorizontalContentAlignment="Stretch" SizeChanged="Layers_SizeChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Here is the code for my items
<UserControl x:Class="ListOverlayItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:Data"
HorizontalContentAlignment="Stretch">
<UserControl.Resources>
<data:Translator x:Key="translatorString" />
</UserControl.Resources>
<Border BorderBrush="Silver" BorderThickness="0,0,0,0" Name="border1" HorizontalAlignment="Stretch">
<Grid x:Name="layout" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<!-- Column 0 -->
<ScrollBar Name="arrangeIcon" FlowDirection="LeftToRight" Maximum="10" SmallChange="1" Value="5" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"/>
<!-- Column 1 -->
<Slider Name="OverlayOpacity" Height="25" Grid.Column="1" Margin="6,0,0,0" Grid.Row="1" Maximum="1" LargeChange="0.1" ValueChanged="OverlayOpacity_ValueChanged"/>
<TextBlock x:Name="OverlayName" Text="{Binding Path=LocationName}" Foreground="#FFF08D2D" Margin="10,2,0,0" Grid.Column="1" FontSize="12" Height="18" VerticalAlignment="Top" />
<!-- Column 3 -->
<Button Name="SettingsButton" Grid.Column="3" Content="{Binding TRK_OV_Settings, Source={StaticResource translatorString}}" VerticalAlignment="Center" Click="SettingsButton_Click" />
<CheckBox x:Name="OverlayEnabled" FlowDirection="LeftToRight" Grid.Column="2" DataContext="{Binding}" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.RowSpan="2" Checked="OverlayEnabled_Checked" Unchecked="OverlayEnabled_Unchecked" />
<TextBlock Name="percentage" Text="100%" FontSize="9" TextAlignment="Right" Grid.Column="2" Grid.Row="1" VerticalAlignment="Center" Margin="2,6,26,6" MinWidth="30"/>
</Grid>
</Border>
Again it seems like my UserControl is correctly able to scale itself its just not getting the command to do so when the parent container is resized.
EDIT: Whoops, removed the WPF tag because it was incorrectly added. I think i've got it down to slider being the culprit. If i set the slider to a fixed size instead of 'stretch' then the item correctly scales just fine. So the question now is how do i force the slider to resize itself.
EDIT2: I know what is causing the issue but i don't know how to solve it. What is happening is the slider that i have in my user control will NOT resize along with the rest of the control unless i change the value of the slider during the resize. The instant i change its value even a fraction it resizes itself automatically. How can i force it to resize?
I determined that the slider was causing the issue and i tried many ways to force the slider to re-draw when it needed to but i was unsuccessful. My final solution was to remove the slider entirely. Works fine now.

TabControl DataTemplate Instance Creation Issues

I have a WPF issue that I don't understand - can anyone help?
The WPF below is used as the ContentTemplate for a standard TabControl and resides within a ResourceDictionary. MyElementItemsControl is a simple derivative of ItemsControl and MyDesignCanvas is a simple derivative of Canvas.
<DataTemplate x:Key="TabContent" x:Shared="False">
<Grid>
<Grid Grid.RowSpan="2">
<ScrollViewer x:Name="contentScrollViewer" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
<Grid>
<View:MyElementItemsControl BorderBrush="Transparent" x:Name="schedulePanel" ItemsSource="{Binding Path=Elements}" Background="White">
<View:MyElementItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<View:MyDesignCanvas Height="1000" Width="1000" HorizontalAlignment="Left" VerticalAlignment="Top"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
Background="Transparent">
</View:MyDesignCanvas>
</ItemsPanelTemplate>
</View:MyElementItemsControl.ItemsPanel>
</View:MyElementItemsControl>
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform>
<ScaleTransform.ScaleX>
<Binding ElementName="SlideZoom" Path="Value" Mode="OneWay"/>
</ScaleTransform.ScaleX>
<ScaleTransform.ScaleY>
<Binding ElementName="SlideZoom" Path="Value" Mode="OneWay"/>
</ScaleTransform.ScaleY>
</ScaleTransform>
</TransformGroup>
</Grid.LayoutTransform>
</Grid>
</ScrollViewer>
</Grid>
<Slider Opacity="0.5" VerticalAlignment="Top" HorizontalAlignment="Left" Width="300" Grid.Row="1" Name="SlideZoom" Orientation="Horizontal" Minimum="0.1" Maximum="3" Value="1">
</Slider>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
</Grid>
</DataTemplate>
When I run the code I get two issues I don't understand:
I seem to only get a single ScrollViewer when I would expect a ScrollViewer per item. So if I add two tab items and make the canvas different sizes, the scroll bars are adjusted to the size of the biggest canvas only. I expected that the Shared=False attribute would create new instances of the template for each tab.
Maybe related to item 1 - if I stick a breakpoint on the constructor of MyDesignCanvas it gets hit when the first tab is added but not when other tabs are added. Only when I start closing tabs does the breakpoint get hit again - I would expect a hit on each tab addition.
I guess I don't really understand data templating enough, so can anyone explain what might be going on or point me at some resources that may help me diagnose this?
Thanks
I've realised what the issue is - the WPF TabControl does internal virtualization of tab content, so has been re-using the tab contents and just changing the data context despite me using Shared=False. See this SO question and this one too for more details.

WPF TextBox Wrapping

I am trying to figure out how to get a textbox to wrap its contents, however the situation isn't quite the same as the typical "it doesn't wrap" scenario. My textbox is contained inside a DataTemplate which is used inside a Telerik RadTabControl instance (using a ContentTemplatePresenter to determine which view to display) and the XAML for the DataTemplate looks like this:
<DataTemplate x:Key="NotesTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Use the box below to record any general notes associated with this item." Style="{StaticResource Default}" />
<TextBox TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" GridRow="1" Margin="20" Text="{Binding Notes, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
The reason I say it doesn't fall under the normal "it doesn't wrap" scenario is it used to wrap until I had to change the view to be resizable to anything to support the varying screen sizes the app will be run on. When I did that the TextBox stopped wrapping because (presumably) as the user types something the TextBox says "I need more space" so the parent obliges and the box continues out to the right indefinitely (although the view gets scrollbars). I tried setting a MaxWidth using Binding/RelativeSource, but since the parent is specifically designed to grow that approach won't work. What I need to have happen is the box should be the width of its' containing parents' VisibleWidth. Meaning, if the Window itself is 1024x768, the TextBox's MaxWidth should be 1024 and any text thereafter would automatically wrap, but if the Window grows to 1280x1024 the box should now be 1280 and the text wrap accordingly. I tried this scenario with this binding expression, but no luck:
MaxWidth="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=ActualWidth}"
The Window size itself isn't growing so if I could get the Window's Width (minus a certain amount to cover the width of the tabs that are part of the TabControl) I believe that would work.
Any ideas?
although the view gets scrollbars
Disable the horizontal scrollView, so it will be forced to wrap. You can try to disable it on the TextBox itself, or on the wrapping Grid.
<DataTemplate x:Key="NotesTemplate">
<Grid ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Use the box below to record any general notes associated with this item." Style="{StaticResource Default}" />
<TextBox TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" Grid.Row="1" Margin="20" Text="{Binding Notes, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>

Use UIElement as Clip in WPF

Please pardon my ignorance- I'm very new to WPF.
I am looking to implement a minor, visual effect in my application that gives the look of "inner" rounded corners. The window in question has a dark border that encapsulates several UIElements, one of which is a StatusBar, located at the bottom of the window. This StatusBar has a dark background that matches the window's border. Above the StatusBar is a content view, which is currently a Grid- its background is semi-transparent (I think that this is something of a constraint- you can see through the content view to the desktop below). I would like for the content view (represented by the transparent, inner area in the figure below) to have the look of rounded corners, though I expect to have to sort of create the illusion myself.
(Can't post the image because I'm a lurker and not a poster- please find the drawing here)
My first approach was to add a Rectangle (filled with the same, dark color as the border) immediately above the StatusBar and to assign a Border with rounded corners to its OpacityMask (similar to the solution proposed by Chris Cavanagh**). Sadly, the effect that is produced is the exact opposite of that which I am trying to achieve.
I understand that the Clip property can be of use in this sort of situation, but it seems to me that using any sort of Geometry will prove to be inadequate as it won't be dynamically sized to the region in which it resides.
EDIT: Including my XAML:
<Grid Background="{StaticResource ClientBg}" Tag="{Binding OverlayVisible}" Style="{StaticResource mainGridStyle}">
<DockPanel LastChildFill="True">
<!-- Translates to a StackPanel with a Menu and a Button -->
<local:FileMenuView DockPanel.Dock="Top" />
<!-- Translates to a StatusBar -->
<local:StatusView DockPanel.Dock="Bottom" />
<!-- Translates to a Grid -->
<local:ContentView />
</DockPanel>
</Grid>
Any pointers are more than welcome- I'm ready to provide more indepth detail if necessary.
** http://www.dotnetkicks.com/wpf/WPF_easy_rounded_corners_for_anything
EDIT: Now I got what you mean. In fact you can use Path + OpacityMask approach. You have to draw "inverted" path, to use it as opacity mask. But I have simpler and faster solution for you :). Use Border + CornerRadius, and fill the gaps with solid paths. Just try the following code in Kaxaml and let me know if this is what you were looking for:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="240"
Height="320"
AllowsTransparency="True"
Background="Transparent"
WindowStyle="None">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<Border Background="Black"/>
<Border Grid.Row="1" BorderBrush="Black" BorderThickness="5">
<Grid>
<Border Background="White" CornerRadius="0, 0, 5, 5" Opacity="0.7"/>
<Path
Width="15"
Height="15"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Data="M10,10 L5,10 L5,5 C4.999,8.343 6.656,10 10,10 z"
Fill="Black"
Stretch="Fill"/>
<Path
Width="15"
Height="15"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Data="M10,10 L5,10 L5,5 C4.999,8.343 6.656,10 10,10 z"
Fill="Black"
Stretch="Fill">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="-1"/>
<TranslateTransform X="15"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Grid>
</Border>
<Border Grid.Row="2" Background="Black"/>
</Grid>
</Window>
PS: You can simplify this solution by avoiding render transforms, but you got the idea.

Resources