0

END GOAL: To print a 2D barcode using WPF and MVVM:

BACKGROUND INFO (Probably not relevant) I have two projects in my solution. One project controls my business logic, and the second project controls the printing logic, creation and printing of labels. I am using named pipes for IPC. I am using MVVM and have a Xaml Template to design the label and at run time I fill its properties and print it. This is all working correctly.

MORE INFO: (Might be relevant) I am using a third party library that creates 2D barcodes. This is working and the call to make the barcode returns a Writable Bitmap

ISSUE: I am trying to databind the writable bitmap to the Image control on my template.

public void FooBar(string[] LabelProperties)
{
    try
    {
        BarcodeWriter writer = new BarcodeWriter()
        {
           Format = BarcodeFormat.PDF_417,
           Options = new ZXing.Common.EncodingOptions
           {
              Height = 50,
              Width = 132,
              Margin = 0
           }
        };

        var wb = writer.Write("Some String");

        System.Windows.Controls.Image newImage = new System.Windows.Controls.Image()
        {
           Height = 50,
           HorizontalAlignment = HorizontalAlignment.Left,
           Name = "image",
           Stretch = Stretch.None,
           VerticalAlignment = VerticalAlignment.Top,
           Width = 132,
           Source = wb,
        };

        this.BarCodeImage = newImage;
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message.ToString().Trim());
    }
}

Its worth noting that I cannot directly place the WritableBitmap to the BarCodeImage.Source

this.BarCodeImage.Source = wb;

because I'm using the MVVM design, the BarCodeImage isn't instantiated so if I try to set something to its Source, it is throws a null reference.

The XAML in the template

<Image Height="50" 
       HorizontalAlignment="Left" 
       Margin="10,2,0,0" 
       Name="image" 
       Stretch="None" 
       VerticalAlignment="Top" 
       Width="132" 
       Source="{Binding lblBarCodeImage}" />

My Thought Because I am having to instantiate a new Controls.Image() and then setting that to the BarCodeImage it is breaking this somehow.

Other Things I can show the other classes and settup to prove my MVVM is settup correctly, but all the other controls are databinding correctly - though they are all strings that I am databinding - no other Image controls.

I have also tried converting the WritableBitmap into a byte array and tried using this solution, but that also did not work

Community
  • 1
  • 1
Brandon
  • 915
  • 4
  • 23
  • 44
  • Where is the `lblBarCodeImage` property declared that you use in the `Source` binding in XAML? Assign `wb` to that property. Besides that, do not create UI elements (like `System.Windows.Controls.Image`) in code behind. Declare them and bind their properties in XAML. – Clemens Aug 01 '16 at 17:06
  • @Clemens I am settings the lblBarCodeImage to the this.BarCodeImage later in the execution. The FooBar call returns an object that has all the values that I want to bind. I then pass that object to my view model where it is set to the lblBarCodeImage property. I think this is working correctly, because I have other strings that I left out of the example that are working correctly. Do you think my problem is creating Controls.image in the code behind? – Brandon Aug 01 '16 at 17:45

2 Answers2

3

Do not not create an Image control in code behind!

Instead, declare a view model property of type ImageSource and in XAML bind the Image control's Source property to that view model property.

View Model:

public class YourViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private ImageSource barCodeImage;

    public ImageSource BarCodeImage
    {
        get { return barCodeImage; }
        set
        {
            barCodeImage = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("BarCodeImage"));
        }
    }

    ...
}

XAML:

<Image ... Source="{Binding BarCodeImage}"/>

In code behind, assign the WriteableBitmap to the BarCodeImage property:

yourViewModel.BarCodeImage = wb;
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • I have solved it, thank you for putting me on the right track. I databound the control's source to a control.Image. (i.e Foo1.Bar = Foo2, instead of Foo1.Bar = Foo2.Bar) Once i went through and corrected this all was well. I didnt have to call the property changed but I will give you credit for the answer as it will probably help someone else! Thank you much sir – Brandon Aug 01 '16 at 18:05
0

You should have a property of BitmapImage like so:

    private BitmapImage photoSelected;

    public BitmapImage PhotoSelected
    {
        get { return photoSelected; }
        set { photoSelected = value; OnPropertyChanged("PhotoSelected"); }
    }

Then on the action that you desire you do this:

PhotoSelected = new BitmapImage(new Uri(@"pack://application:,,,/Images/4.png"));

Replace /Images/4.png with the path to your image starting at the solution level. For example, this is what my solution tree looks like to reach that point:

Solution Tree

XAML Used for binding:

<Image x:Name="BitMapImage" Source="{Binding PhotoSelected, Mode=TwoWay}" RenderOptions.BitmapScalingMode="HighQuality"/>
Kikootwo
  • 360
  • 2
  • 14
  • Setting `Mode=TwoWay` on the Source binding makes absolutely no sense. It has no effect, because an Image control never changes the value of its Source property. Besides that, why not simply write `PhotoSelected = wb;` in an attempt to refer to the question? The type of the `PhotoSelected` property should therefore be `ImageSource`, not `BitmapImage`. `ImageSource` is the base class, hence the property would be more flexibly usable. – Clemens Aug 01 '16 at 17:13
  • This is a snippet from one of my projects. I required the TwoWay because the entire project wasn't entirely MVVM so there were some actions in the code behind that could change the image, and I needed that image accessible in my VM. My goal wasn't to provide him a code complete answer, but rather share my experience to point him in the right direction. – Kikootwo Aug 01 '16 at 17:19
  • Why don't you just take the 2 seconds to edit it out? – Clemens Aug 01 '16 at 17:22
  • Thank you for the response, but I would rather not save this image before setting it as the source to my Image. These barcodes will be created too often to try and save each one. – Brandon Aug 01 '16 at 17:46
  • You don't need to save it. That's how I did it in my example, but, as long as you bind 'PhotoSelected' (or whatever your bound property is) to a BitmapImage. [Here are instructions for converting Writeable Bitmap to Bitmap](http://stackoverflow.com/questions/14161665/how-do-i-convert-a-writeablebitmap-object-to-a-bitmapimage-object-in-wpf). – Kikootwo Aug 01 '16 at 19:00