3

I created this DataTemplate, but I can't figure out how to add a style with a DataTrigger to the img variable.

I would like img to show different images depending on the value of Suppliers[i].Stock (int)

Resource Icons

Properties.Resources.InStock     => Suppliers[i].Stock > 0
Properties.Resources.OutOfStock  => Suppliers[i].Stock = 0
Properties.Resources.Unknown     => Suppliers[i].Stock = null

My code so far.

private DataTemplate GetStockTemplate(int i)
{
    var template = new DataTemplate();

    var wp = new FrameworkElementFactory(typeof (WrapPanel));
    wp.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Right);
    wp.SetValue(WrapPanel.OrientationProperty, Orientation.Horizontal);

    var tx = new FrameworkElementFactory(typeof (TextBox));
    tx.SetBinding(TextBox.TextProperty, new Binding("Suppliers[" + i + "].Stock") {StringFormat = "{0:n0}"});
    tx.SetValue(TextBoxBase.IsReadOnlyProperty, true);
    tx.SetValue(Control.BorderThicknessProperty, new Thickness(0));
    tx.SetValue(Control.BackgroundProperty, Brushes.Transparent);
    tx.SetValue(TextBox.TextAlignmentProperty, TextAlignment.Right);

    wp.AppendChild(tx);

    var img = new FrameworkElementFactory(typeof (Image));
    wp.AppendChild(img);

    template.VisualTree = wp;
    template.Seal();
    return template;
}

The way I think the trigger could work is, create a default style showing the InStock icon, Then have two triggers on for Stock = null and another for Stock = 0

Since I'm doing this dynamically I can't use xaml, and I got everything else working with DataTemplate.

Solution

With help from @akjoshi this is what I ended up using.

var img = new FrameworkElementFactory(typeof(Image));
            var binding = new Binding("Suppliers[" + i + "].Stock") {Converter = new StockIconConverter()};
img.SetBinding(Image.SourceProperty, binding);
wp.AppendChild(img); 

class StockIconConverter :IValueConverter 
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null || (int)value < 0)
            return ConvertIconToBitmapImage(Properties.Resources.Unknown);

        return ConvertIconToBitmapImage((int)value == 0 ? Properties.Resources.OutOfStock : Properties.Resources.InStock);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #region Helper
    private static BitmapImage ConvertIconToBitmapImage(Icon icon)
    {
        var bitmap = icon.ToBitmap();
        var ms = new MemoryStream();
        bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
        var bImg = new BitmapImage();

        bImg.BeginInit();
        bImg.StreamSource = new MemoryStream(ms.ToArray());
        bImg.CreateOptions = BitmapCreateOptions.None;
        bImg.CacheOption = BitmapCacheOption.Default;
        bImg.EndInit();
        bImg.Freeze();

        ms.Close();

        return bImg;
    }
    #endregion
}
akjoshi
  • 15,374
  • 13
  • 103
  • 121
gulbaek
  • 2,481
  • 13
  • 44
  • 65
  • Glad it helped; +1 for posting the answer. – akjoshi Jul 17 '12 at 12:02
  • One suggestion, you can create the `BitmapImage` for an icon once and cache it in converter itself and use that instead of creating it again and again, otherwise you will endup creating same BitmapImage `n` number of times and degrade performance. – akjoshi Jul 17 '12 at 12:02

1 Answers1

6

I think your idea is right, code should be something like this -

var img = new FrameworkElementFactory(typeof(Image)); 
img.SetValue(Image.SourceProperty, "InStockImagePath");

Style style = new Style();
style.TargetType = typeof(Image);

DataTrigger zeroDataTrigger = new DataTrigger();
zeroDataTrigger.Binding = new Binding("Suppliers[" + i + "].Stock");
zeroDataTrigger.Value = 0;
zeroDataTrigger.Setters.Add(new Setter(Image.SourceProperty, "OutOfStockImagePath"));

DataTrigger nullDataTrigger = new DataTrigger();
nullDataTrigger.Binding = new Binding("Suppliers[" + i + "].Stock");
nullDataTrigger.Value = null;
nullDataTrigger.Setters.Add(new Setter(Image.SourceProperty, "unknownImagePath"));

style.Triggers.Add(zeroDataTrigger);
style.Triggers.Add(nullDataTrigger);

img.SetValue(Image.StyleProperty, style);
wp.AppendChild(img); 

I suggest you to look at following great article from Sacha, related to this -

WPF: How to create Styles in code/and magical Content

As a sidenote, as FrameworkElementFactory is deprecated, it would be better to define this style in Resources/xaml and use FindResource() to set it. If some redesigning can make you achieve this then I would suggest you to do that.

Community
  • 1
  • 1
akjoshi
  • 15,374
  • 13
  • 103
  • 121
  • 1
    Yeah, I should find the time to rewrite my code into xaml, since its so hard working with FrameworkElementFactory compared to xaml, and there is almost no documentation. – gulbaek Jul 17 '12 at 10:05
  • Hmm, the DataTrigger's is not firing. And the Stock sends and PropertyChanged event. – gulbaek Jul 17 '12 at 10:17
  • @gulbaek Hard to say whats wrong, just make sure image paths are correct URl's and binding source is also there in DataContext; You can also try to add a converter to Trigger binding and see if it's fired o not and what value is coming in it. – akjoshi Jul 17 '12 at 10:33