Thursday, December 18, 2014

Type based templating

            <DataTemplate DataType="{x:Type vm:SampleViewModel}">
                <DataTemplate.Resources>
                    <DataTemplate DataType="{x:Type vm:SampleViewModel}" x:Key="BoolSampleViewModelTemplate">
                        <CheckBox IsChecked="{Binding Value}"/>
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type vm:SampleViewModel}" x:Key="StringSampleViewModelTemplate">
                        <TextBox Text="{Binding Value}"></TextBox>
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type vm:SampleViewModel}" x:Key="DoubleSampleViewModelTemplate">
                        <TextBox Text="{Binding Value, StringFormat='{}{0:C}'}"/>
                    </DataTemplate>
                </DataTemplate.Resources>
                <ContentControl Content="{Binding}" Name="presenter" />
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding Type}" Value="{x:Type sys:Boolean}">
                        <Setter TargetName="presenter" Property="ContentTemplate" Value="{StaticResource BoolSampleViewModelTemplate}" ></Setter>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Type}" Value="{x:Type sys:Double}">
                        <Setter TargetName="presenter" Property="ContentTemplate" Value="{StaticResource DoubleSampleViewModelTemplate}" ></Setter>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Type}" Value="{x:Type sys:String}">
                        <Setter TargetName="presenter" Property="ContentTemplate" Value="{StaticResource StringSampleViewModelTemplate}" ></Setter>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>

Monday, March 18, 2013

Alternative to OuterGlow BitmapEffect in WPF 4.0

WPF 4.0 has removed support for Bitmap effects as the are software rendered and cause performance issues. No alternatives are provided for these bitmap effects. I had to work on finding out alternative for OuterGlowEffect and I ended up tweaking DropShadowEffect to achieve this.

To start with I tried Obsolete Bitmap effect just to see if they work with warning or something. So below is what I wrote:

<TextBlock Text="Outer glow effect demo" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center">
     <TextBlock.BitmapEffect>
           <OuterGlowBitmapEffect GlowColor="Red" ></OuterGlowBitmapEffect>
     </TextBlock.BitmapEffect>
</<TextBlock>
The app run fine but no effect got applied. WPF 4.0 just ignores these bitmap effects.


If ShadowDepth is set to zero and BlurRadius is set to around 30 for DropShadowEffect it seems to gives a bit of glow around the control but its not quite a match for OuterGlowBitmapEffect.

<TextBlock Text="Outer glow effect demo" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center">
      <TextBlock.Effect>
         <DropShadowEffect BlurRadius="30" Opacity="1" ShadowDepth="0" Color="Red"></DropShadowEffect>
      </TextBlock.Effect>
</TextBlock>



I actually got stuck at this until found the trick to make it a lot closer to OuterGlowBitmapEffect. I put the Textblock inside a grid and applied the same on the grid but still not quite there, so I repeated the same once more and it started looking very much Like an OuterGlowBitmapEffect. Below is the xaml and screenshot of how it looks after this.

       <Grid>
        <Grid.Resources>
            <DropShadowEffect x:Key="glowEffect" BlurRadius="30" Opacity="1" ShadowDepth="0" Color="Red"></DropShadowEffect>
        </Grid.Resources>
        <Grid Effect="{StaticResource glowEffect}">
            <Grid Effect="{StaticResource glowEffect}">
                <TextBlock Text="Outer glow effect demo" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center" Effect="{StaticResource glowEffect}"/>
            </Grid>
        </Grid>
    </Grid>

I hope this will be helpful to anyone looking for alternative to OuterGlowBitmapEffect.

Sunday, February 12, 2012

IsPressed Trigger on any WPF control

WPF provides IsPressed Property on button that can be used to write triggers based on its value. There is no such propery available on other control. I recently had to show a DropShadowEffect when a Border is pressed. I used solution described below which can be used for any other WPF control as well that derives from FrameworkElement. I have used attached properties for this.


public class FrameworkElementExt
{
public static readonly DependencyProperty IsPressedProperty = DependencyProperty.RegisterAttached("IsPressed", typeof(bool),
typeof(FrameworkElementExt), new PropertyMetadata(false));

public static readonly DependencyProperty AttachIsPressedProperty = DependencyProperty.RegisterAttached("AttachIsPressed", typeof(bool), typeof(FrameworkElementExt), new PropertyMetadata(false, PropertyChangedCallback));

public static void PropertyChangedCallback(DependencyObject depObj, DependencyPropertyChangedEventArgs args)
{
   FrameworkElement element = (FrameworkElement)depObj;
   if (element != null)
   {
     if ((bool)args.NewValue)
     {
       element.MouseDown +=
new MouseButtonEventHandler(element_MouseDown);
       element.MouseUp += new MouseButtonEventHandler(element_MouseUp);
       element.MouseLeave += new MouseEventHandler(element_MouseLeave);
     }
     else
     {
       element.MouseDown -=
new MouseButtonEventHandler(element_MouseDown);
       element.MouseUp -= new MouseButtonEventHandler(element_MouseUp);
      element.MouseLeave -= new MouseEventHandler(element_MouseLeave);
      }
   }
}

static void element_MouseLeave(object sender, MouseEventArgs e)
{
  FrameworkElement element = (FrameworkElement)sender;
  if (element != null)
  {
    element.SetValue(IsPressedProperty,
false);
  }
}
static void element_MouseUp(object sender, MouseButtonEventArgs e)
{
  FrameworkElement element = (FrameworkElement)sender;
  if (element != null)
  {
    element.SetValue(IsPressedProperty,
false);
  }
}
static void element_MouseDown(object sender, MouseButtonEventArgs e)
{
  FrameworkElement element = (FrameworkElement)sender;
  if (element != null)
  {
    element.SetValue(IsPressedProperty,
true);
  }
}
public static bool GetIsPressed(UIElement element)
{
  return (bool)element.GetValue(IsPressedProperty);
}
public static void SetIsPressed(UIElement element, bool val)
{
  element.SetValue(IsPressedProperty, val);
}
public static bool GetAttachIsPressed(UIElement element)
{
  return (bool)element.GetValue(AttachIsPressedProperty);
}
public static void SetAttachIsPressed(UIElement element, bool val)
{
  element.SetValue(AttachIsPressedProperty, val);
}
}

Effect is defined in Resorces
<DropShadowEffect x:Key="effect" ShadowDepth="0" Color="White" BlurRadius="20" />


Now Define The Trigger as below, notice that AttachIsPressed needs to be set to true. 

<Border CornerRadius="10" local:FrameworkElementExt.AttachIsPressed="True">
<Border.Style>
  <Style TargetType="{x:Type Border}">
    <Style.Triggers>
       <Trigger Property="local:FrameworkElementExt.IsPressed" Value="True">
         <Setter Property="Effect" Value="{StaticResource effect}"/>
      </Trigger>
    </Style.Triggers>
  </Style>
</Border.Style>

</Border>

Wednesday, January 18, 2012

Run WPF application without App.xaml

Delete the existing App.xaml file and write a new class file(lets call it StartUp.cs) that looks like below:
    public class StartUp : Application
    {
        [System.STAThreadAttribute()]
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public static void Main()
        {
            StartUp app = new StartUp();
            app.InitializeComponent();
            app.Run();
        }
        public void InitializeComponent()
        {
            this.StartupUri = new Uri("MainWindow.xaml", System.UriKind.Relative);
        }
    }

Note that this class inherits System.Windows.Application

Tuesday, January 10, 2012

Wire any WPF Event to Command on ViewModel in MVVM

In the last post I explained how to write a behaviour to hook a commmand to an event. That works well but a new behaviour is needed to written for each event. I tried a generic approach to have a single behaviour that can be used to wire any event to a command. It comes witha little performance penalty as reflection is used to get the event and attached a delegate to that event. Below is the code:

using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.Windows.Controls;
using System.Reflection;
using System.Diagnostics;

public class CommandExecuter
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(CommandExecuter), new PropertyMetadata(CommandPropertyChangedCallback));

public static readonly DependencyProperty OnEventProperty = DependencyProperty.RegisterAttached("OnEvent", typeof(string), typeof(CommandExecuter));

public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(CommandExecuter));

public static void CommandPropertyChangedCallback(DependencyObject depObj, DependencyPropertyChangedEventArgs args)
{
  string onEvent = (string)depObj.GetValue(OnEventProperty);
  Debug.Assert(onEvent != null, "OnEvent must be set.");
  var eventInfo = depObj.GetType().GetEvent(onEvent);
  if (eventInfo != null)
  {
    var mInfo = typeof(CommandExecuter).GetMethod("OnRoutedEvent", BindingFlags.NonPublic | BindingFlags.Static);
    eventInfo.GetAddMethod().Invoke(depObj, new object[] { Delegate.CreateDelegate(eventInfo.EventHandlerType, mInfo) });
  }
  else
  {
    Debug.Fail(string.Format("{0} is not found on object {1}", onEvent, depObj.GetType()));

}

}
public static ICommand GetCommand(UIElement element)
{
   return (ICommand)element.GetValue(CommandProperty);
}
public static void SetCommand(UIElement element, ICommand command)
{
   element.SetValue(CommandProperty, command);
}
public static string GetOnEvent(UIElement element)
{
return (string)element.GetValue(OnEventProperty);
}
public static void SetOnEvent(UIElement element, string evnt)
{
   element.SetValue(OnEventProperty, evnt);
}
public static object GetCommandParameter(UIElement element)
{
  return (object)element.GetValue(CommandParameterProperty);
}
public static void SetCommandParameter(UIElement element, object commandParam)
{
   element.SetValue(CommandParameterProperty, commandParam);
}
private static void OnRoutedEvent(object sender, RoutedEventArgs e)
{
  UIElement element = (UIElement)sender;
  if (element != null)
  {    ICommand command = element.GetValue(CommandProperty) as ICommand;
    if (command != null && command.CanExecute(element.GetValue(CommandParameterProperty)))
    {
      command.Execute(element.GetValue(CommandParameterProperty));
    }
  }
}
}


Below code shows how to set the command on the controls for any event:   (local: is namespace prefix)
<ComboBox local:CommandExecuter.Command="{Binding CommandImpl}" local:CommandExecuter.OnEvent="SelectionChanged" ></ComboBox>

<Button local:CommandExecuter.Command="{Binding AnotherCommandImpl}" local:CommandExecuter.OnEvent="MouseEnter" local:CommandExecuter.CommandParameter="{x:Static null}"></Button>