WPF: 实现 disabled 时自动变灰的 Image

用过 WinForms 的人都知道,如果一个 Button 或者 MenuItem 里包含了一个图片,那么当Button 或者MenuItem 被 disabled 时,包含的图片会自动变成灰色并且变淡。惊人的是,WPF 里并没有现成的这项功能;而且,摸索发现实现这个功能并不方便。以下就来归纳几种让Image 在 disabled 时自动变灰的解决方案。这里着重于图标的自动变灰效果。

方案一:采用两张图片

  最直截了当的方案是,对每一个图标,手工生成两张图片:一张是正常显示的图片,一张是 disabled 时的图片。当 IsEnabled 属性变化时,选择适当的图片来显示。

  这里一项有用的细节是,UIElement 类的IsEnabled 属性有一个特点,即如果 parent control 是 disabled,那么 child control 也自动就是 disabled;也就是说,包含在Button 或者MenuItem 里的 Image 控件只需要监测自己的 IsEnabled 属性就行了,而不需要去找Button 或者MenuItem 的 IsEnabled 属性。

  优点:直截了当,容易理解;并且理论上可以选择一张毫不相干的图片在 disabled 时显示,尽管在实际操作中还是选择正常图片的灰色效果图为好。

  缺点:为每个图标管理两张图片,增加了管理的复杂性;增加执行文件的尺寸;而且在外部生成灰度图片也不见得方便。


方案二:正常图片本身是灰度图片的情形

  如果正常图片本身就是灰度(黑白)图片,那么实现自动变灰很容易,因为这里的“变灰”实际上是“变淡”。WPF 里的 UIElement 类有一个Opacity 属性,将这个值设为0.5,就能达到与系统一致的灰度效果。比如,下面的简单代码就能使黑白图片在 disabled 时自动变淡:

    <!-- Define the following style in resources -->
    <Style x:Key="AutoGray" TargetType="{x:Type Image}">
      <Style.Triggers>
        <Trigger Property="IsEnabled" Value="False">
          <Setter Property="Opacity" Value="0.5"/>
        </Trigger>
      </Style.Triggers>
    </Style>
     
    <!-- Apply the style to the image -->
    <Image Style="AutoGray" Source="..."/>

  优点:实现方便,且无须管理额外的图片。

  缺点:仅适用于正常图片本身就是灰度图片的情形。


方案三:正常图片本身是彩色图片的情形

  如果图片本身是彩色的,那么实现自动变灰就十分麻烦了。必须通过程序生成图片的灰度版。一个示意性的 XAML 例子如下(摘自这里并稍做修改):

 <Button IsEnabled="False">
      <Image Opacity="0.5">
        <Image.Source>
          <FormatConvertedBitmap DestinationFormat="Gray8">
            <FormatConvertedBitmap.Source>
              <BitmapImage UriSource="Images\OutdentHS.png" />
            </FormatConvertedBitmap.Source>
          </FormatConvertedBitmap>
        </Image.Source>
        <Image.OpacityMask>
          <ImageBrush>
            <ImageBrush.ImageSource>
              <BitmapImage UriSource="Images\OutdentHS.png" />
            </ImageBrush.ImageSource>
          </ImageBrush>
        </Image.OpacityMask>
      </Image>
    </Button>

  这段例子有两个要点:

  要点一:采用 WPF 自带的 FormatConvertedBitmap 类,将原始(彩色)图片转换为灰度图片。这样做的好处是我们不需要关心怎样将 RGB 值变为灰度值;但是缺点是该转换会 discard 图片的透明度信息(alpha channel),使原本透明的像素变成黑色。因此我们必须关注下面的要点二。

  要点二:采用 Image.OpacityMask 属性和 ImageBrush 类,将原始图片的透明度信息应用到灰度转换后的图片上。

  上述方法看上去还是有点罗嗦的;如果有兴趣的可以自己实现像素的格式转换,保留 alpha channel。在此不详述。

  如果有很多个图标需要自动变灰,那么给每一个图标写这么一大段 XAML 是不实际的。因此我们首先想到能否定义一个 Style 来实现?很可惜,答案是不能。因为在Style 中必须通过Trigger 来改变Image 的 Source 属性,而在Image 元素中也势必要定义其Source 属性;而 WPF 规定如果元素本身定义了一个属性,那么它的Style 里定义的属性就被忽略;因此无法通过定义Style 来改变 Image 的Source,从而实现自动变灰的效果。

  因此,想要批量解决图片的自动变灰,只好自定义一个控件,比如从 Image 类派生一个 AutoGrayImage 类。一个现成的例子在这里。另外一个略加改进的实现在这里

  总结一下方案三的优缺点:

  优点:适用于彩色图片;开发简单(只需将 Image 改成 AutoGrayImage)。

  缺点:如果直接使用 FormatConvertedBitmap 类,则还需要用到 ImageBrush,有点浪费。

 

WPF 实现不规则的ImageButton及当Disable自动变灰

实现一个可任意设置图片的Image Button, 当状态为disable时(IsEnabled=False),图片变灰。

 

1.首先新建一个类:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows.Controls;
  6. using System.Windows.Data;
  7. using System.Globalization;
  8. using System.Windows.Media.Imaging;
  9.  
  10. namespace WpfAppImageButton.Control
  11. {
  12. class ImageButton : Button
  13. {
  14. private string m_imagepath;
  15.  
  16. public string ImgPath
  17. {
  18. get { return m_imagepath; }
  19. set { m_imagepath = value; }
  20. }
  21. }
  22.  
  23. public class Path2UriSource : IValueConverter
  24. {
  25. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  26. {
  27. string relativePath = value as string;
  28. if (string.IsNullOrEmpty(relativePath))
  29. return null;
  30. relativePath = "pack://application:,,,/" + relativePath;
  31. Uri uri = new Uri(relativePath, UriKind.RelativeOrAbsolute);
  32.  
  33. FormatConvertedBitmap bitmap = new FormatConvertedBitmap();
  34. bitmap.BeginInit();
  35. bitmap.Source = new BitmapImage(uri);
  36. bitmap.DestinationFormat = System.Windows.Media.PixelFormats.Gray32Float;
  37. bitmap.EndInit();
  38. return bitmap;
  39. }
  40.  
  41. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  42. {
  43. return value;
  44. }
  45. }
  46.  
  47. public class Path2ImageBrush : IValueConverter
  48. {
  49. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  50. {
  51. string relativePath = value as string;
  52. if (string.IsNullOrEmpty(relativePath))
  53. return null;
  54. relativePath = "pack://application:,,,/" + relativePath;
  55. Uri uri = new Uri(relativePath, UriKind.RelativeOrAbsolute);
  56. BitmapImage source = new BitmapImage(uri);
  57. System.Windows.Media.ImageBrush imgBursh = new System.Windows.Media.ImageBrush(source);
  58. return imgBursh;
  59. }
  60.  
  61. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  62. {
  63. return value;
  64. }
  65. }
  66. }

 

2. XAML 设置模板:

  1.  
  2. <ControlTemplate x:Key="ImageButtonTemplate" TargetType="{x:Type lControl:ImageButton}">
  3. <Grid>
  4. <Rectangle x:Name="bgrect" Fill="Transparent" Opacity="0.5"/>
  5. <Image x:Name="img" HorizontalAlignment="Stretch" Source="{Binding ImgPath, RelativeSource={RelativeSource TemplatedParent}}" Stretch="Uniform" />
  6. </Grid>
  7.  
  8. <ControlTemplate.Triggers>
  9. <Trigger Property="IsEnabled" Value="False">
  10. <Setter TargetName="img" Property="Source" Value="{Binding ImgPath, RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource Path2UriSource}}"/>
  11. <Setter TargetName="img" Property="OpacityMask" Value="{Binding ImgPath, RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource Path2ImageBrush}}"/>
  12. </Trigger>
  13. <Trigger Property="IsMouseOver" Value="True">
  14. <Setter TargetName="bgrect" Property="Fill" Value="#FFC800"/>
  15. </Trigger>
  16. <Trigger Property="IsPressed" Value="True">
  17. <Setter TargetName="bgrect" Property="Fill" Value="#22EF1F"/>
  18. </Trigger>
  19. </ControlTemplate.Triggers>
  20. </ControlTemplate>



3. 效果图:



 

4. XAML全部代码

    1. <Window
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Luna"
    5. x:Class="WpfAppImageButton.MainWindow"
    6. x:Name="Window"
    7. Title="MainWindow"
    8. xmlns:lControl="clr-namespace:WpfAppImageButton.Control"
    9. xmlns:ef="http://schemas.microsoft.com/expression/2010/effects"
    10. Width="510" Height="281" WindowStartupLocation="CenterScreen" WindowStyle="ThreeDBorderWindow" Background="#FF339A14">
    11.  
    12. <Window.Resources>
    13. <lControl:Path2UriSource x:Key="Path2UriSource"/>
    14. <lControl:Path2ImageBrush x:Key="Path2ImageBrush"/>
    15.  
    16. <ControlTemplate x:Key="ImageButtonTemplate" TargetType="{x:Type lControl:ImageButton}">
    17. <Grid>
    18. <Rectangle x:Name="bgrect" Fill="Transparent" Opacity="0.5"/>
    19. <Image x:Name="img" HorizontalAlignment="Stretch" Source="{Binding ImgPath, RelativeSource={RelativeSource TemplatedParent}}" Stretch="Uniform" />
    20. </Grid>
    21.  
    22. <ControlTemplate.Triggers>
    23. <Trigger Property="IsEnabled" Value="False">
    24. <Setter TargetName="img" Property="Source" Value="{Binding ImgPath, RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource Path2UriSource}}"/>
    25. <Setter TargetName="img" Property="OpacityMask" Value="{Binding ImgPath, RelativeSource={RelativeSource TemplatedParent},Converter={StaticResource Path2ImageBrush}}"/>
    26. </Trigger>
    27. <Trigger Property="IsMouseOver" Value="True">
    28. <Setter TargetName="bgrect" Property="Fill" Value="#FFC800"/>
    29. </Trigger>
    30. <Trigger Property="IsPressed" Value="True">
    31. <Setter TargetName="bgrect" Property="Fill" Value="#22EF1F"/>
    32. </Trigger>
    33. </ControlTemplate.Triggers>
    34. </ControlTemplate>
    35. </Window.Resources>
    36.  
    37. <Grid x:Name="LayoutRoot">
    38.  
    39. <Button Content="Disable all button" Height="47" HorizontalAlignment="Left" Margin="327,12,0,0" Name="button1" VerticalAlignment="Top" Width="135" Click="button1_Click" />
    40. <lControl:ImageButton Content="Button" Height="64" Focusable="False" HorizontalAlignment="Left" Margin="20,12,0,0" x:Name="button2" VerticalAlignment="Top" Width="202"
    41. ImgPath="../Images/colorButton.png" Template="{StaticResource ImageButtonTemplate}"/>
    42. <lControl:ImageButton Content="Button" Height="47" Focusable="False" HorizontalAlignment="Left" Margin="20,95,0,0" x:Name="button3" VerticalAlignment="Top" Width="151"
    43. ImgPath="../Images/OK.png" Template="{StaticResource ImageButtonTemplate}"/>
    44. <lControl:ImageButton Content="Button" Focusable="False" Height="130" HorizontalAlignment="Left" Margin="211,86,0,0" x:Name="button4" VerticalAlignment="Top" Width="144"
    45. ImgPath="../Images/select.png" Template="{StaticResource ImageButtonTemplate}"/>
    46. </Grid>
    47. </Window>

wpf 让透明图片变灰 wpf disable image

前台:

<Image Name="myImg" Source="../Themes/img.png"></Image>


后台:

ImageSource isource = myImg.Source;
                               
                FormatConvertedBitmap fcb = new FormatConvertedBitmap((BitmapSource)isource, PixelFormats.Gray32Float, null, 0);
                myImg.Source = fcb;
                myImg.OpacityMask = new ImageBrush(isource);

参考资料:
http://social.msdn.microsoft.com/forums/en-US/wpf/thread/1961fcab-14f4-4345-87b2-dd2bc517898c/
http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.formatconvertedbitmap.aspx
http://msdn.microsoft.com/zh-cn/library/system.windows.media.drawinggroup.opacitymask.aspx


方案四:采用 BitmapEffect 应用渲染效果

  这是一个大炮打蚊子的方案,因为它用到了 DirectX 的 PixelShader,从而充分(还是过度?)发挥了 WPF 的界面渲染特性。我并没有自己试验这个方案。不过有几个 Tutorial 可以看一看:

  A PixelShader that means business (2009) 概括地介绍了采用 DirectX pixel shader 来实现灰度效果的方案。

  Grayscale Effect - A Pixel Shader Effect in WPF (2008) 一步一步地详细讲解了实现步骤。

  A Series on GPU-based Effects for WPF (2008) 系统地介绍了在 WPF 中应用渲染效果的知识。

  作为一个感性的认识,采用这种方案的 XAML 是下面这个样子:

    <Image Opacity="1" Source="{TemplateBinding Image}">
      <Image.Effect>
        <effects:GrayScaleEffect DesaturationFactor="0"/>
      </Image.Effect>
    </Image>

  优点:XAML 模型美观;无需生成额外的灰度图片;可以控制色彩度的水平(尽管在实际中没什么用)。

  缺点:依赖于 DirectX,开发复杂;对于小图标,有点大炮打蚊子,其 overhead 未经评估。
总结

  上面介绍了四种使图标自动变灰的方案。比较这几种方案,我认为“方案三”是最佳的,因为它以相对直观的逻辑实现了需要的效果,而且使用起来非常简单。

  最后还是要惊叹一下,WPF 居然连这么简单的功能都要用户自己造轮子实现,实在是叹为观止。期待 WPF 5.0 能有所改善吧。

---------------------
作者:fancidev
来源:CSDN
原文:https://blog.csdn.net/u010553621/article/details/8948638
版权声明:本文为博主原创文章,转载请附上博文链接!

 

Grayscale Effect - A Pixel Shader Effect in WPF

 

This post will describe how Effects (pixel shaders) are used in WPF. I will give a simple step-by-step example of a grayscale effect. You must have .NET Framework 3.5 sp1 or later installed for this to work. You also need DirectX SDK installed to be able to compile the pixel shader effect.

Download Sample Project and source code here.
Download effect here (add it to your project and use a grayscale effect on your WPF components)

image  image   image

1. New Project
Start Visual Studio and create a new WPF Application. (Targeting the .NET 3.5 Framework)

image

2. Add new project. (Targeting the .NET 3.5 Framework)
In your solution, add a new project, a Class Library-project. Name your project "GrayscaleEffect". Now you will have the following view in your Solution Explorer:

image

3. Prepare the Grayscale Effect.
First add some references to the GrayscaleEffect-project:
PresentationCore
PresentationFramework
WindowsBase
Delete the autogenerated Class1.cs-file and instead create a new C# class and name it "GrayscaleEffect.cs". This will be an empty class. Now lets add the C#-part of the effect:

using System;
using System.Windows.Media.Effects;
using System.Windows;
using System.Windows.Media;

namespace GrayscaleEffect
{
public class GrayscaleEffect : ShaderEffect
{
private static PixelShader _pixelShader = new PixelShader() { UriSource = new Uri(@"pack://application:,,,/GrayscaleEffect;component/GrayscaleEffect.ps") };

public GrayscaleEffect()
{
PixelShader = _pixelShader;

UpdateShaderValue(InputProperty);
UpdateShaderValue(DesaturationFactorProperty);
}

public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(GrayscaleEffect), 0);
public Brush Input
{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}

public static readonly DependencyProperty DesaturationFactorProperty = DependencyProperty.Register("DesaturationFactor", typeof(double), typeof(GrayscaleEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(0), CoerceDesaturationFactor));
public double DesaturationFactor
{
get { return (double)GetValue(DesaturationFactorProperty); }
set { SetValue(DesaturationFactorProperty, value); }
}

private static object CoerceDesaturationFactor(DependencyObject d, object value)
{
GrayscaleEffect effect = (GrayscaleEffect)d;
double newFactor = (double)value;

if (newFactor < 0.0 || newFactor > 1.0)
{
return effect.DesaturationFactor;
}

return newFactor;
}
}
}

 



This is the C# side of the effect and will be the interface to the shader effect. This class and its properties (dependency properties) can be used directly from WPF and XAML. Note that the properties can be animated in the same way as other dependency properties. The difference is that we use a special type for the Input property and for our custom property we just refer to a register on the GPU, but we really do not need to know that ;). I really love the simplicity!



4. Add the effect files.

In the GrayscaleEffect-project, add two new files (Add -> New Item). GrayscaleEffect.fx and GrayscaleEffect.ps as Text-file. Make sure that GrayscaleEffect.ps is set as a Resource (in Build Action) in Properties. NOTE: The GrayscaleEffect.fx must be in ANSI format. Convert it to ANSI by opening the file in Notepad and save it, select ANSI format in the save dialog.



5. Now open the GrayscaleEffect.fx and add the following code (this is HLSL - High Level Shader Language):

sampler2D implicitInput : register(s0);
float factor : register(c0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D(implicitInput, uv);
float gray = color.r * 0.3 + color.g * 0.59 + color.b *0.11;

float4 result;
result.r = (color.r - gray) * factor + gray;
result.g = (color.g - gray) * factor + gray;
result.b = (color.b - gray) * factor + gray;
result.a = color.a;

return result;
}

 




This is our pixel shader. As we can see at the top implicitInput refers to one registry and a float named factor refers to another register. This is the register that we set in our C#-file (the last parameter in RegisterPixelShaderSamplerProperty(,,0) and the parameter in PixelShaderConstantCallback(0)). Below is our entry point the the shader file. We take a position as input and the first thing we do is to get the value in the "texture" at this position.


The magic below this is out grayscale alogrithm. We first create a grayscale value from the red, green and blue channel. Then we set this value in the output value depending on the factor parameter. The factor parameter is the DesaturationFactor in the C#-file and we use it to create a desaturation effect and a gradient between color and grayscale mode.



6. Compile the pixel shader.

We have created the pixel shader effect but we need to compile it to use it. Note that we can do this outside of Visual Studio, but we don't want that, so do the following. Open the project-properties for the GrayscaleEffect-project and select "Build Events". Under Pre-build event command line, add the following:


"$(DXSDK_DIR)Utilities\Bin\x86\fxc.exe" /T ps_2_0 /E main /Fo"$(SolutionDir)GrayscaleEffect/GrayscaleEffect.ps" "$(SolutionDir)GrayscaleEffect/GrayscaleEffect.fx"


If you now compile the project your effect should compile just fine. But we are not using it so lets use it:



7. Now in WpfApplication1.

First add a reference to the GrayscaleEffect-project from the WpfApplication1-project.


We also want to use some images that we can apply out effect on. NOTE that the effect is not limited to images, we can use any control or panel in WPF, and that's cool!!! However, we use images because they are nice ;). Add a folder the the WpfApplication1 called "images". Add a few (~4) images to this folder (Add -> Existing item), name them like img1.jpg, img2.jpg... .


Your Solution Explorer should look like this:


image 
Now add the following XAML to Window1.xaml:



<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:effect="clr-namespace:GrayscaleEffect;assembly=GrayscaleEffect"
Title="Grayscale Effect - Dotway (www.dotway.se)" Height="400" Width="480">

<Window.Resources>

<DataTemplate x:Key="itemTemplate">
<Grid Width="225" Margin="3">
<Border BorderBrush="White" BorderThickness="2">
<Image Source="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image.Effect>
<effect:GrayscaleEffect x:Name="grayscaleEffect"/>
</Image.Effect>
<Image.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1.0" Duration="0:0:0.3" AccelerationRatio="0.10" DecelerationRatio="0.25" Storyboard.TargetName="grayscaleEffect" Storyboard.TargetProperty="(effect:GrayscaleEffect.DesaturationFactor)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0.0" Duration="0:0:4" AccelerationRatio="0.10" DecelerationRatio="0.25" Storyboard.TargetName="grayscaleEffect" Storyboard.TargetProperty="(effect:GrayscaleEffect.DesaturationFactor)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</Border>
</Grid>
</DataTemplate>

</Window.Resources>

<Grid Background="Black">

<ItemsControl x:Name="myItemsControl" ItemTemplate="{StaticResource itemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

</Grid>

</Window>

 

 

We have a ItemsControl that have a WrapPanel as layout panel. We also have a template for each item in the ItemsControl. In the template two triggers have been added with animations. These animations will animate the DesaturationFactor property which will affect the shader effect.

To use the images add the following to the code-behind file:

public Window1()
{
InitializeComponent();

List<string> images = new List<string>();
for (int i = 0; i < 4; i++)
{
images.Add("images/img" + i + ".jpg");
}
myItemsControl.ItemsSource = images;
}

 




A PixelShader that means business

 

Maybe you heard that PixelShaders are a feature of Silverlight 3 … which is coming our way very soon.

Maybe you’re like I was, thinking that PixelShaders have no place in a business application. Hey, I love those rippling water demos as much as the next guy. But I’ll be looking for new employment the morning after I give my application a psychedelic makeover.

Then my buddy Joe Gershgorin introduced me to the “Grayscale Effect”, a PixelShader effect that de-saturates the colors of the targeted image, “graying” it out.

You say “So what?”

Here’s a hint: What’s the look of a disabled button? It’s gray.

I used to have one image for the active button state and another image for the disabled button state. If I’ve got 10 buttons, that’s 20 images to maintain. That’s double the payload winging over the wire to my Silverlight client.

It takes time (and skills I lack) to produce the first image; adding a disabled version is another step I can do without.

Now, instead of swapping button images when the enabled state changes, I add or remove the grayscale effect.

I’m not sure where Joe found the code for this effect. The first mention I can find is a blog post by Anders Bursjöö from June 2008. I don’t know if Anders post was first; I can confirm that it will take you through the details of the effect as it relates to WPF … which details are the same for Silverlight.

There is no need for me to repeat what Anders had to say or the many who followed him and took their turns at explaining it. I can also recommend Greg Schechter’s series of May 2008 posts on custom GPU-based effects for WPF.

Now put that knowledge to good use by shedding image bloat and waste your creativity on some other project.

Oh … you wanted code?

Dude ... it's XAML. We'll be here all day.

Ok, here are two snippets for your delectation. Imagine we're inside ResourceDictionary defining a style for the button.

Here is the image definition that will use template binding to pick up the image assigned to the button:

 <Image 
x:Name="ImagePresenter"
Margin="0,0,0,0"
Opacity="1"
Source="{TemplateBinding Image}"
Width="32"
Height="32"
HorizontalAlignment="Center"
Stretch="Uniform"
>
<Image.Effect>
<effects:GrayScaleEffect DesaturationFactor="1"/>
</Image.Effect>
</Image>



Notice how we added the effect, setting the DesaturationFactor to one (meaning … ahem … no desaturation). GrayScaleEffect is the class straight from Anders example.

 

Now let's look at the pertinent "Disabled" state governed by the VisualStateManager:

 

<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames
BeginTime="00:00:00" Duration="00:00:00"
Storyboard.TargetName="ImagePresenter"
Storyboard.TargetProperty="(UIElement.Effect).(GrayScaleEffect.DesaturationFactor)">

<DiscreteDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>

 

Notice how we’re identifying the effect and its DesaturationFactor property using attached property syntax. Setting the factor to zero results in the gray effect. We’re not using animation so the duration is zero-time.

 

Wouldn’t it be great if there were a one-liner to specify a no-animation Storyboard or just to skip the Storyboard altogether? Maybe someday.




Now just compile and run your new effect!

BUT, how is this really useful except that it gives us "pretty images". Well, I am a fan of focus and context/master-detail implementations. It is all about putting focus to some part of my GUI and leave the rest unfocused and in a context. With the color/grayscale we can put focus to an item by giving it color and all the rest (context) is grayscale. 
This is not the end of it. In a later post I hope I can show you how to interact with the GUI after applying a pixel shader effect. The only thing you have to do is to implement EffeceMapping in you C#-file, more on that later. 
I hope this will help someone, otherwise contact the consultants at Dotway (www.dotway.se)! ;)

 

posted @ 2019-04-08 13:22  JolinPiggy  阅读(1001)  评论(0)    收藏  举报