vladeck's junkyard

designer wannabe


1 Comment

SearchTextBox Behavior

Damn, this was a tricky one – a bit of coding, a sprinkle of XAML and here it is. And it works. You can set this Behavior to any TextBox and turn it into Windows 7 search box like thingy. You can define your own Search and Clear icons (ImageSource) and (if you don’t want search-as-you-type) Search command (ICommand). The good thing is that it doesn’t affect the style/template you set for the TextBox, as it is implemented as Adorner.

Download here (rename .DOC to .RAR – wordpress limitation).


2 Comments

Button Popup Behavior

Another behavior that I wrote for my project. The code is simple and there aren’t many things to configure, but it is a good starting point for a nice button popup (many other examples I’ve seen are coded as custom controls; the same can be accomplished using this behavior):

namespace Foo
{
    public class ButtonPopupBehavior : Behavior<Button>
    {
        private readonly Popup popup_;

        public static DependencyProperty PopupProperty =
            DependencyProperty.Register(
                "Popup",
                typeof(UIElement),
                typeof(ButtonPopupBehavior),
                null
            );

        public ButtonPopupBehavior()
        {
            popup_ = new Popup() { StaysOpen = false };
        }

        public UIElement Popup
        {
            get { return (UIElement)GetValue(PopupProperty); }
            set { SetValue(PopupProperty, value); }
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            
            AssociatedObject.Click += OnClick;
            AssociatedObject.IsVisibleChanged += OnIsVisibleChanged;

            popup_.PlacementTarget = AssociatedObject;
            popup_.Child = Popup;
        }

        protected override void OnDetaching()
        {
            AssociatedObject.Click -= OnClick;
            AssociatedObject.IsVisibleChanged -= OnIsVisibleChanged;
            
            base.OnDetaching();
        }

        private void OnClick(object sender, RoutedEventArgs e)
        {
            popup_.IsOpen = true;
        }

        private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (!(bool)e.NewValue)
                popup_.IsOpen = false;
        }
    }
}


Leave a comment

Conditional referencing in .NET

If you ever happen to stumble upon a problem of having the need to support multiple versions of the same assembly in your .NET project, read on… Take these two assemblies for example:

Foo.dll (v1.0)

namespace Foo
{
    class Bar
    {
        public void Do()
        {
            Console.WriteLine("Hello, from v1.0 of Bar");
        }
    }
}

Foo.dll (v2.0)

namespace Foo
{
    class Bar
    {
        public void Do(string parameter)
        {
            Console.WriteLine("Hello, from v2.0 of Bar with parameter: {0}", parameter);
        }
    }
}

So, now we have two Foo.dll assemblies that are of different version and we want to use them in our project. First, copy these assemblies to project directory and put them under v1 and v2 directories (at the same level as the .csproj file); then, open the .csproj file and add the following configurations (I’ll do this for DEBUG versions – just copy DEBUG that is already there and modify it; the RELEASE is similar):

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug - v1|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\Debug\v1\</OutputPath>
    <DefineConstants>TRACE;DEBUG;V_ONE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug - v2|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\Debug\v2\</OutputPath>
    <DefineConstants>TRACE;DEBUG;V_TWO</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <ErrorReport>prompt</ErrorReport>
</PropertyGroup>

And here comes the good part, just below previous modification:

<Choose>
    <When Condition=" '$(Configuration)' == 'Debug - v1'">
      <ItemGroup>
        <Reference Include="Foo">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>v1\Foo.dll</HintPath>
        </Reference>
      </ItemGroup>
    </When>
    <When Condition=" '$(Configuration)' == 'Debug - v2'">
      <ItemGroup>
        <Reference Include="Foo">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>v2\Foo.dll</HintPath>
        </Reference>
      </ItemGroup>
    </When>
</Choose>

Now you are ready to choose your build configuration, and have code support multiple version of the same assembly:

var b = new Foo.Bar();

#if (V_ONE)
   b.Do();
#elif (V_TWO)
   b.Do("Hello");
#endif


Leave a comment

Animated ErrorInfo Behavior

Ever since I wrote Animated Height Behavior, I wanted to create Behavior that will give me nice eye-candy for all my IDataErrorInfo validation needs. You can see the result in the video below, as well as the code of the Behavior. Please note that the image I’m using here is from the Resources (just add 16×16 png image to Resources and name it Error16). GetBitmapSource() is Extension that I wrote for converting System.Drawing.Bitmap to BitmapSource.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace Foo
{
    public class AnimatedErrorInfoBehavior : Behavior<FrameworkElement>
    {
        private readonly TextBlock textBlock_;
        private Grid errorGrid_;

        private readonly DoubleAnimation increaseHeightAnimation_;
        private readonly Storyboard increaseHeightStoryboard_;

        private readonly DoubleAnimation showAnimation_;
        private readonly Storyboard showStoryboard_;

        private readonly DoubleAnimation decreaseHeightAnimation_;
        private readonly Storyboard descreaseHeightStoryboard_;

        private readonly DoubleAnimation hideAnimation_;
        private readonly Storyboard hideStoryboard_;

        private readonly Dictionary<string, int> errors_;

        public AnimatedErrorInfoBehavior()
        {
            errors_ = new Dictionary<string, int>();
            textBlock_ = new TextBlock();

            increaseHeightAnimation_ = new DoubleAnimation(
                0.0,
                23.0,
                TimeSpan.FromMilliseconds(200)
            );

            increaseHeightAnimation_.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
            increaseHeightStoryboard_ = new Storyboard();

            Storyboard.SetTarget(increaseHeightAnimation_, errorGrid_);
            Storyboard.SetTargetProperty(increaseHeightAnimation_, new PropertyPath(FrameworkElement.HeightProperty));
            increaseHeightStoryboard_.Children.Add(increaseHeightAnimation_);

            showAnimation_ = new DoubleAnimation(0.0, 1.0, TimeSpan.FromMilliseconds(200));
            showAnimation_.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseIn };
            showStoryboard_ = new Storyboard();

            Storyboard.SetTarget(showAnimation_, textBlock_);
            Storyboard.SetTargetProperty(showAnimation_, new PropertyPath(UIElement.OpacityProperty));
            showStoryboard_.Children.Add(showAnimation_);

            decreaseHeightAnimation_ = new DoubleAnimation(
                23.0,
                0.0,
                TimeSpan.FromMilliseconds(100)
            );

            descreaseHeightStoryboard_ = new Storyboard();

            Storyboard.SetTarget(decreaseHeightAnimation_, errorGrid_);
            Storyboard.SetTargetProperty(decreaseHeightAnimation_, new PropertyPath(FrameworkElement.HeightProperty));
            descreaseHeightStoryboard_.Children.Add(decreaseHeightAnimation_);

            hideAnimation_ = new DoubleAnimation(1.0, 0.0, TimeSpan.FromMilliseconds(100));
            hideAnimation_.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };

            hideStoryboard_ = new Storyboard();

            Storyboard.SetTarget(hideAnimation_, textBlock_);
            Storyboard.SetTargetProperty(hideAnimation_, new PropertyPath(UIElement.OpacityProperty));
            hideStoryboard_.Children.Add(hideAnimation_);
        }

        protected override void OnAttached()
        {
            var parent = VisualTreeHelper.GetParent(AssociatedObject) as Grid;

            if (parent == null)
                return;

            var row_index = Grid.GetRow(AssociatedObject);
            var row_span = Grid.GetRowSpan(AssociatedObject);

            var column_index = Grid.GetColumn(AssociatedObject);
            var column_span = Grid.GetColumnSpan(AssociatedObject);

            parent.Children.Remove(AssociatedObject);

            var grid = new Grid();
            grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
            grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });

            Grid.SetRow(grid, row_index);
            Grid.SetRowSpan(grid, row_span);

            Grid.SetColumn(grid, column_index);
            Grid.SetColumnSpan(grid, column_span);

            grid.Children.Add(AssociatedObject);
            Grid.SetRow(AssociatedObject, 0);

            errorGrid_ = new Grid();
            errorGrid_.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
            errorGrid_.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });

            errorGrid_.Height = 0;

            var image = new Image();
            image.Source = Resources.Error16.GetBitmapSource();
            image.Margin = new Thickness(0,0,2,7);

            errorGrid_.Children.Add(image);
            Grid.SetColumn(image, 0);

            textBlock_.Foreground = Brushes.Red;

            errorGrid_.Children.Add(textBlock_);
            Grid.SetColumn(textBlock_, 1);

            grid.Children.Add(errorGrid_);
            Grid.SetRow(errorGrid_, 1);

            parent.Children.Add(grid);

            Validation.AddErrorHandler(AssociatedObject, OnError);
            Validation.SetErrorTemplate(AssociatedObject, null);

            base.OnAttached();
        }

        private void OnError(object sender, ValidationErrorEventArgs e)
        {
            var error_msg = e.Error.ErrorContent.ToString();

            if (e.Action == ValidationErrorEventAction.Added)
            {
                if (errors_.ContainsKey(error_msg))
                    errors_[error_msg]++;
                else
                    errors_.Add(error_msg, 1);

                if (errors_[error_msg] == 1)
                {
                    textBlock_.Text = e.Error.ErrorContent.ToString();

                    increaseHeightStoryboard_.Begin(errorGrid_);
                    showStoryboard_.Begin(textBlock_);
                }
            }

            if (e.Action == ValidationErrorEventAction.Removed)
            {
                errors_[error_msg]--;

                if (errors_[error_msg] == 0)
                {
                    descreaseHeightStoryboard_.Begin(errorGrid_);
                    hideStoryboard_.Begin(textBlock_);
                }
            }
        }
    }
}


2 Comments

System.Drawing.Bitmap GetBitmapSource() Extension – Convert System.Drawing.Bitmap to BitmapSource

If you are like me, you probably keep bitmaps (png files) of your WPF project in Resources. Since Resources uses System.Drawing.Bitmap for storing bitmaps, I wrote (some code modifications were done by Bojan) this little extension that will convert bitmap resource to BitmapSource.

using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Foo
{
    public static class BitmapExtension
    {
        public static BitmapSource GetBitmapSource(this Bitmap image)
        {
            var rect = new Rectangle(0, 0, image.Width, image.Height);
            var bitmap_data = image.LockBits(rect, ImageLockMode.ReadOnly, image.PixelFormat);

            try
            {
                BitmapPalette palette = null;
                
                if (image.Palette.Entries.Length > 0)
                {
                    var palette_colors = image.Palette.Entries.Select(entry => System.Windows.Media.Color.FromArgb(entry.A, entry.R, entry.G, entry.B)).ToList();
                    palette = new BitmapPalette(palette_colors);
                }

                return BitmapSource.Create(
                    image.Width,
                    image.Height,
                    image.HorizontalResolution,
                    image.VerticalResolution,
                    ConvertPixelFormat(image.PixelFormat),
                    palette,
                    bitmap_data.Scan0,
                    bitmap_data.Stride * image.Height,
                    bitmap_data.Stride
                );
            }
            finally
            {
                image.UnlockBits(bitmap_data);
            }
        }

        private static System.Windows.Media.PixelFormat ConvertPixelFormat(System.Drawing.Imaging.PixelFormat sourceFormat)
        {
            switch (sourceFormat)
            {
                case System.Drawing.Imaging.PixelFormat.Format24bppRgb:
                    return PixelFormats.Bgr24;

                case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
                    return PixelFormats.Bgra32;

                case System.Drawing.Imaging.PixelFormat.Format32bppRgb:
                    return PixelFormats.Bgr32;
            }

            return new System.Windows.Media.PixelFormat();
        }
    }
}


Leave a comment

Animated Height Behavior

On one project that I’m working on, I had a need to animate height (simulating Visible/Not Visible state of ‘overlay’ and edit dialogs) of FrameworkElement. So I wrote this little behavior that does just that. You can use IsVisible to control (using binding) ‘Visibility’, as well as ShowDuration and HideDuration to control duration of animation.

using System;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Media.Animation;

namespace Foo
{
    public class AnimatedHeightBehavior : Behavior<FrameworkElement>
    {
        private static DoubleAnimation showAnimation_;
        private static readonly Storyboard showStoryboard_ = new Storyboard();

        private static DoubleAnimation hideAnimation_;
        private static readonly Storyboard hideStoryboard_ = new Storyboard();

        public static DependencyProperty IsVisibleProperty =
            DependencyProperty.Register(
                "IsVisible",
                typeof(bool),
                typeof(AnimatedHeightBehavior),
                new PropertyMetadata(false, OnShowChanged)
            );

        public static DependencyProperty ShowDurationProperty =
            DependencyProperty.Register(
                "ShowDuration",
                typeof(int),
                typeof(AnimatedHeightBehavior),
                new PropertyMetadata(200)
        );

        public static DependencyProperty HideDurationProperty =
            DependencyProperty.Register(
                "HideDuration",
                typeof(int),
                typeof(AnimatedHeightBehavior),
                new PropertyMetadata(50)
        );

        public bool IsVisible
        {
            get { return (bool)GetValue(IsVisibleProperty); }
            set { SetValue(IsVisibleProperty, value); }
        }

        public int ShowDuration
        {
            get { return (int)GetValue(HideDurationProperty); }
            set { SetValue(HideDurationProperty, value); }
        }

        public int HideDuration
        {
            get { return (int)GetValue(ShowDurationProperty); }
            set { SetValue(ShowDurationProperty, value); }
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.Loaded += OnLoaded;
        }

        protected override void OnDetaching()
        {
            AssociatedObject.Loaded -= OnLoaded;
            base.OnDetaching();
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            if (!IsVisible)
                AssociatedObject.Height = 0.0;

            showAnimation_ = new DoubleAnimation(
                0.0,
                AssociatedObject.ActualHeight,
                TimeSpan.FromMilliseconds((int)GetValue(ShowDurationProperty))
            );

            showAnimation_.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };

            Storyboard.SetTarget(showAnimation_, AssociatedObject);
            Storyboard.SetTargetProperty(showAnimation_, new PropertyPath(FrameworkElement.HeightProperty));
            showStoryboard_.Children.Add(showAnimation_);

            hideAnimation_ = new DoubleAnimation(
                AssociatedObject.ActualHeight,
                0.0,
                TimeSpan.FromMilliseconds((int)GetValue(HideDurationProperty))
            );

            Storyboard.SetTarget(hideAnimation_, AssociatedObject);
            Storyboard.SetTargetProperty(hideAnimation_, new PropertyPath(FrameworkElement.HeightProperty));
            hideStoryboard_.Children.Add(hideAnimation_);
        }

        private static void OnShowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = ((AnimatedHeightBehavior)d).AssociatedObject;

            if ((bool)e.NewValue)
                showStoryboard_.Begin(element);
            else
                hideStoryboard_.Begin(element);
        }
    }
}