Answered a bitmap created from scratch is flashing

  • Saturday, August 04, 2012 4:28 PM
     
      Has Code

    I'm studying WPF and I'm at the point in which I can create a bitmap from code

    but I'm not at the point in which to make it visible

    So I tried to guess the right code. In XAML there is a wrappanel, with a button and two (other) images the sources of which are two resource file.

    In the code of button_click, I wrote:

        Dim bmp As New RenderTargetBitmap(300, 150, 300, 300, PixelFormats.Pbgra32)
        Dim ell As New Ellipse With {.Fill = Brushes.Red}
        ell.Measure(New Size(96, 48))
        ell.Arrange(New Rect(0, 0, 96, 48))
        bmp.Render(ell)
        DirectCast(sender, Button).Background = New ImageBrush(bmp)

    The resulting effect is that button background is the new image, but it is vanishing and reapparing. Why? And how can I solve?

All Replies

  • Saturday, August 04, 2012 4:40 PM
     
      Has Code

    well, I've found that I can add an object by this way:

        Dim img As New Image
        img.Source = bmp
        ' mainWP is the root wrappanel
        mainWP.Children.Add(img)

    I miss to understand why button background appears and disappears...
  • Monday, August 06, 2012 12:49 AM
    Moderator
     
     Answered Has Code

    Prepare for code dump...

    MainWindow.xaml

    <Window
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" x:Class="WpfApplication48.MainWindow"
            Title="MainWindow" Height="350" Width="525">
    	<Window.Resources>
            
            <!-- button 2 -->
            
    		<LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
    			<GradientStop Color="#F3F3F3" Offset="0"/>
    			<GradientStop Color="#EBEBEB" Offset="0.5"/>
    			<GradientStop Color="#DDDDDD" Offset="0.5"/>
    			<GradientStop Color="#CDCDCD" Offset="1"/>
    		</LinearGradientBrush>
    		<SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070"/>
    		<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
    			<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
    			<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
    			<Setter Property="BorderThickness" Value="1"/>
    			<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    			<Setter Property="HorizontalContentAlignment" Value="Center"/>
    			<Setter Property="VerticalContentAlignment" Value="Center"/>
    			<Setter Property="Padding" Value="1"/>
    			<Setter Property="Template">
    				<Setter.Value>
    					<ControlTemplate TargetType="{x:Type Button}">
    						<Microsoft_Windows_Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" RenderDefaulted="{TemplateBinding IsDefaulted}">
    							<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    						</Microsoft_Windows_Themes:ButtonChrome>
    						<ControlTemplate.Triggers>
    <!--
    							<Trigger Property="IsKeyboardFocused" Value="true">
    								<Setter Property="RenderDefaulted" TargetName="Chrome" Value="true"/>
    							</Trigger>
    							<Trigger Property="ToggleButton.IsChecked" Value="true">
    								<Setter Property="RenderPressed" TargetName="Chrome" Value="true"/>
    							</Trigger>
    -->
    							<Trigger Property="IsEnabled" Value="false">
    								<Setter Property="Foreground" Value="#ADADAD"/>
    							</Trigger>
    						</ControlTemplate.Triggers>
    					</ControlTemplate>
    				</Setter.Value>
    			</Setter>
    		</Style>
    		
            <!-- button 3 -->
    
            <LinearGradientBrush x:Key="NormalBrush" EndPoint="0,1" StartPoint="0,0">
                <GradientStop Color="#EEE" Offset="0.0"/>
                <GradientStop Color="#CCC" Offset="1.0"/>
            </LinearGradientBrush>
            <LinearGradientBrush x:Key="NormalBorderBrush" EndPoint="0,1" StartPoint="0,0">
                <GradientStop Color="#CCC" Offset="0.0"/>
                <GradientStop Color="#444" Offset="1.0"/>
            </LinearGradientBrush>
            <LinearGradientBrush x:Key="PressedBrush" EndPoint="0,1" StartPoint="0,0">
                <GradientStop Color="#BBB" Offset="0.0"/>
                <GradientStop Color="#EEE" Offset="0.1"/>
                <GradientStop Color="#EEE" Offset="0.9"/>
                <GradientStop Color="#FFF" Offset="1.0"/>
            </LinearGradientBrush>
            <LinearGradientBrush x:Key="PressedBorderBrush" EndPoint="0,1" StartPoint="0,0">
                <GradientStop Color="#444" Offset="0.0"/>
                <GradientStop Color="#888" Offset="1.0"/>
            </LinearGradientBrush>
            <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888"/>
            <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE"/>
            <SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA"/>
            
            <Style x:Key="SimpleButton" TargetType="{x:Type Button}" BasedOn="{x:Null}">
    		<Setter Property="Background" Value="{DynamicResource NormalBrush}"/>
    		<Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
    		<Setter Property="Template">
    			<Setter.Value>
    				<ControlTemplate TargetType="{x:Type Button}">
    					<Grid x:Name="Grid">
    						<Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"/>
    						<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
    					</Grid>
    					<ControlTemplate.Triggers>
    						<Trigger Property="IsKeyboardFocused" Value="true">
    							<Setter Property="BorderBrush" Value="{DynamicResource DefaultedBorderBrush}" TargetName="Border"/>
    						</Trigger>
    						<Trigger Property="IsMouseOver" Value="true">
    							<Setter Property="Background" Value="{DynamicResource MouseOverBrush}" TargetName="Border"/>
    						</Trigger>
    						<Trigger Property="IsPressed" Value="true">
    							<Setter Property="Background" Value="{DynamicResource PressedBrush}" TargetName="Border"/>
    							<Setter Property="BorderBrush" Value="{DynamicResource PressedBorderBrush}" TargetName="Border"/>
    						</Trigger>
    						<Trigger Property="IsEnabled" Value="true"/>
    						<Trigger Property="IsEnabled" Value="false">
    							<Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border"/>
    							<Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border"/>
    							<Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}"/>
    						</Trigger>
    					</ControlTemplate.Triggers>
    				</ControlTemplate>
    			</Setter.Value>
    		</Setter>
    	</Style>
    	
    	</Window.Resources>
        <WrapPanel>
            <Button Content="Button1" x:Name="button1" Width="100" Height="50" />
            <Button Content="Button1b" x:Name="button1b" Width="100" Height="50" Focusable="False" />
            <Button Content="Button2" x:Name="button2"  Width="100" Height="50" Style="{DynamicResource ButtonStyle1}"/>
            <Button Content="Button3" x:Name="button3"  Width="100" Height="50" Style="{DynamicResource SimpleButton}"/>
        </WrapPanel>
    </Window>

      

    MainWindow.xaml.cs

    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Shapes;
    
    namespace WpfApplication48
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                    var bmp = new RenderTargetBitmap(300, 150, 300, 300, PixelFormats.Pbgra32);
    
                    var ell = new Ellipse {Fill = Brushes.Red};
                    ell.Measure(new Size(96, 48));
                    ell.Arrange(new Rect(0, 0, 96, 48));
                    bmp.Render(ell);
    
                    button1.Background = new ImageBrush(bmp);
                    button1b.Background = new ImageBrush(bmp);
                    button2.Background = new ImageBrush(bmp);
                    button3.Background = new ImageBrush(bmp);
            }
        }
    }

     

    This is C# as you can see, but it is doing exactly the same as yours, it's not the code where your problems are.

    You are replacing the button background, which is changed when rendering of various states.

     

    The standard Button uses ButtonChrome in it's template, which changes Background in various ways. One way is when the Button is in focus, a behaviour of ButtonChrome is to flash, as you found, and is replicated in button1.

     

    The simplest solution is to set Focusable="false", as shown in buton1b.

     

    The reason it is happening, as explained is because of various Triggers and behaviours. Button2 uses the default style, that I extracted using ExpressionBlend. In ButtonStyle1, you will see the ButtonChrome and where I have commented out two of the triggers, but notice the RenderMouseOver & RenderPressed, they both also affect button Background, and you'd have to disable them too.

     

    A final example (button3) is using Expression Blend's built in SimpleStyles.xaml. I have included the "SimpleButton" style, which does not use ButtonChrome, and shows all the triggers that are affecting button state and Background, so you can fiddle with effects easier from that.

     

    In fact when you see the templates above, I'm sure you can see that you can replace the Border in the ControlTemplate with an Ellipse, and get rid of your "ellipse image rendering" code completely ;)

     

    Further reading...

    http://msdn.microsoft.com/en-us/library/ms753328.aspx - Button Styles and Templates

    http://msdn.microsoft.com/en-us/library/ms745683.aspx - Styling and Templating

     

    I hope that covers everything you needed to know :)

     

    Regards,
    Pete


    #PEJL